changeset 820:81d186c34e54

Merge
author alanb
date Fri, 14 Nov 2008 09:43:00 +0000
parents 1b828db8e180 dcb8d806d731
children a7d137d96cf5
files .hgtags
diffstat 20 files changed, 749 insertions(+), 154 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Mon Nov 10 12:28:47 2008 +0000
+++ b/.hgtags	Fri Nov 14 09:43:00 2008 +0000
@@ -13,6 +13,7 @@
 134fd1a656ea85acd1f97f6700f75029b9b472a0 jdk7-b36
 14f50aee4989b75934d385c56a83da0c23d2f68b jdk7-b37
 cc5f810b5af8a3a83b0df5a29d9e24d7a0ff8086 jdk7-b38
+4e51997582effa006dde5c6d8b8820b2045b9c7f jdk7-b39
 939fd6c3984ac3b34b8613608b4b07c89193a3d7 nio2-b91
 5996f2328c7c88e5e01edf26969775b9f1467e61 nio2-b92
 7c735eade49f2e8de90bbda0d119f0146704832a nio2-b93
--- a/src/share/classes/com/sun/net/ssl/internal/www/protocol/https/HttpsURLConnectionOldImpl.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/src/share/classes/com/sun/net/ssl/internal/www/protocol/https/HttpsURLConnectionOldImpl.java	Fri Nov 14 09:43:00 2008 +0000
@@ -497,6 +497,10 @@
         delegate.setFixedLengthStreamingMode(contentLength);
     }
 
+    public void setFixedLengthStreamingMode(long contentLength) {
+        delegate.setFixedLengthStreamingMode(contentLength);
+    }
+
     public void setChunkedStreamingMode (int chunklen) {
         delegate.setChunkedStreamingMode(chunklen);
     }
--- a/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java	Fri Nov 14 09:43:00 2008 +0000
@@ -86,6 +86,8 @@
  * the principal name from the configuration is used. In the
  * case where the principal property is not set and the principal
  * entry also does not exist, the user is prompted for the name.
+ * When this property of entry is set, and <code>useTicketCache</code>
+ * is set to true, only TGT belonging to this principal is used.
  *
  * <p> The following is a list of configuration options supported
  * for <code>Krb5LoginModule</code>:
@@ -101,18 +103,19 @@
  * to false if you do not want this module to use the ticket cache.
  * (Default is False).
  * This module will
- * search for the tickect
+ * search for the ticket
  * cache in the following locations:
- * For Windows 2000, it will use Local Security Authority (LSA) API
- * to get the TGT. On Solaris and Linux
+ * On Solaris and Linux
  * it will look for the ticket cache in /tmp/krb5cc_<code>uid</code>
  * where the uid is numeric user
  * identifier. If the ticket cache is
- * not available in either of the above locations, or if we are on a
- * different Windows platform,  it will look for the cache as
+ * not available in the above location, or if we are on a
+ * Windows platform, it will look for the cache as
  * {user.home}{file.separator}krb5cc_{user.name}.
  * You can override the ticket cache location by using
- * <code>ticketCache</code>
+ * <code>ticketCache</code>.
+ * For Windows, if a ticket cannot be retrieved from the file ticket cache,
+ * it will use Local Security Authority (LSA) API to get the TGT.
  * <P>
  * <dt><b><code>ticketCache</code></b>:</dt>
  * <dd>Set this to the name of the ticket
@@ -129,20 +132,20 @@
  * <dt><b><code>doNotPrompt</code></b>:</dt>
  * <dd>Set this to true if you do not want to be
  * prompted for the password
- * if credentials can
- * not be obtained from the cache or keytab.(Default is false)
- * If set to true authentication will fail if credentials can
- * not be obtained from the cache or keytab.</dd>
+ * if credentials can not be obtained from the cache, the keytab,
+ * or through shared state.(Default is false)
+ * If set to true, credential must be obtained through cache, keytab,
+ * or shared state. Otherwise, authentication will fail.</dd>
  * <P>
  * <dt><b><code>useKeyTab</code></b>:</dt>
  * <dd>Set this to true if you
  * want the module to get the principal's key from the
  * the keytab.(default value is False)
- * If <code>keyatb</code>
+ * If <code>keytab</code>
  * is not set then
  * the module will locate the keytab from the
- * Kerberos configuration file.</dd>
- * If it is not specifed in the Kerberos configuration file
+ * Kerberos configuration file.
+ * If it is not specified in the Kerberos configuration file
  * then it will look for the file
  * <code>{user.home}{file.separator}</code>krb5.keytab.</dd>
  * <P>
@@ -210,20 +213,34 @@
  *                   exist for the username and password in the shared
  *                   state, or if authentication fails.
  *
- *    clearPass     if, true, this <code>LoginModule</code> clears the
- *                  username and password stored in the module's shared
- *                  state  after both phases of authentication
- *                  (login and commit)  have completed.
+ *    clearPass      if, true, this LoginModule clears the
+ *                   username and password stored in the module's shared
+ *                   state  after both phases of authentication
+ *                   (login and commit) have completed.
  * </pre>
+ * <p>If the principal system property or key is already provided, the value of
+ * "javax.security.auth.login.name" in the shared state is ignored.
+ * <p>When multiple mechanisms to retrieve a ticket or key is provided, the
+ * preference order looks like this:
+ * <ol>
+ * <li>ticket cache
+ * <li>keytab
+ * <li>shared state
+ * <li>user prompt
+ * </ol>
+ * <p>Note that if any step fails, it will fallback to the next step.
+ * There's only one exception, it the shared state step fails and
+ * <code>useFirstPass</code>=true, no user prompt is made.
  * <p>Examples of some configuration values for Krb5LoginModule in
  * JAAS config file and the results are:
  * <ul>
  * <p> <code>doNotPrompt</code>=true;
  * </ul>
- * <p> This is an illegal combination since <code>useTicketCache</code>
- * is not set and the user can not be prompted for the password.
+ * <p> This is an illegal combination since none of <code>useTicketCache</code>,
+ * <code>useKeyTab</code>, <code>useFirstPass</code> and <code>tryFirstPass</code>
+ * is set and the user can not be prompted for the password.
  *<ul>
- * <p> <code>ticketCache</code> = < filename >;
+ * <p> <code>ticketCache</code> = &lt;filename&gt;;
  *</ul>
  * <p> This is an illegal combination since <code>useTicketCache</code>
  * is not set to true and the ticketCache is set. A configuration error
@@ -240,9 +257,9 @@
  *</ul>
  * <p> This is an illegal combination since  <code>storeKey</code> is set to
  * true but the key can not be obtained either by prompting the user or from
- * the keytab.A configuration error will occur.
+ * the keytab, or from the shared state. A configuration error will occur.
  * <ul>
- * <p>  <code>keyTab</code> = < filename > <code>doNotPrompt</code>=true ;
+ * <p>  <code>keyTab</code> = &lt;filename&gt; <code>doNotPrompt</code>=true ;
  * </ul>
  * <p>This is an illegal combination since useKeyTab is not set to true and
  * the keyTab is set. A configuration error will occur.
@@ -260,7 +277,7 @@
  * with the principal and TGT. If the TGT is not available,
  * do not prompt the user, instead fail the authentication.
  * <ul>
- * <p><code>principal</code>=< name ><code>useTicketCache</code> = true
+ * <p><code>principal</code>=&lt;name&gt;<code>useTicketCache</code> = true
  * <code>doNotPrompt</code>=true;
  *</ul>
  * <p> Get the TGT from the default cache for the principal and populate the
@@ -269,9 +286,9 @@
  * authentication will fail.
  * <ul>
  * <p> <code>useTicketCache</code> = true
- * <code>ticketCache</code>=< file name ><code>useKeyTab</code> = true
- * <code> keyTab</code>=< keytab filename >
- * <code>principal</code> = < principal name >
+ * <code>ticketCache</code>=&lt;file name&gt;<code>useKeyTab</code> = true
+ * <code> keyTab</code>=&lt;keytab filename&gt;
+ * <code>principal</code> = &lt;principal name&gt;
  * <code>doNotPrompt</code>=true;
  *</ul>
  * <p>  Search the cache for the principal's TGT. If it is not available
@@ -281,7 +298,7 @@
  * If the key is not available or valid then authentication will fail.
  * <ul>
  * <p><code>useTicketCache</code> = true
- * <code>ticketCache</code>=< file name >
+ * <code>ticketCache</code>=&lt;file name&gt;
  *</ul>
  * <p> The TGT will be obtained from the cache specified.
  * The Kerberos principal name used will be the principal name in
@@ -292,8 +309,8 @@
  * The Subject will be populated with the TGT.
  *<ul>
  * <p> <code>useKeyTab</code> = true
- * <code>keyTab</code>=< keytab filename >
- * <code>principal</code>= < principal name >
+ * <code>keyTab</code>=&lt;keytab filename&gt;
+ * <code>principal</code>= &lt;principal name&gt;
  * <code>storeKey</code>=true;
  *</ul>
  * <p>  The key for the principal will be retrieved from the keytab.
@@ -303,7 +320,7 @@
  * password entered.
  * <ul>
  * <p> <code>useKeyTab</code> = true
- * <code>keyTab</code>=< keytabname >
+ * <code>keyTab</code>=&lt;keytabname&gt;
  * <code>storeKey</code>=true
  * <code>doNotPrompt</code>=true;
  *</ul>
@@ -316,21 +333,23 @@
  * Subject's private credentials set. Otherwise the authentication will
  * fail.
  *<ul>
- * <p><code>useKeyTab</code> = true
- * <code>keyTab</code>=< file name > <code>storeKey</code>=true
- * <code>principal</code>= < principal name >
+ * <p>
  * <code>useTicketCache</code>=true
- * <code>ticketCache</code>=< file name >;
+ * <code>ticketCache</code>=&lt;file name&gt;;
+ * <code>useKeyTab</code> = true
+ * <code>keyTab</code>=&lt;file name&gt; <code>storeKey</code>=true
+ * <code>principal</code>= &lt;principal name&gt;
  *</ul>
- * <p>The principal's key will be retrieved from the keytab and added
- * to the <code>Subject</code>'s private credentials. If the key
- * is not available, the
- * user will be prompted for the password; the key derived from the password
- * will be added to the Subject's private credentials set. The
- * client's TGT will be retrieved from the ticket cache and added to the
+ * <p>
+ * The client's TGT will be retrieved from the ticket cache and added to the
  * <code>Subject</code>'s private credentials. If the TGT is not available
- * in the ticket cache, it will be obtained using the authentication
+ * in the ticket cache, or the TGT's client name does not match the principal
+ * name, Java will use a secret key to obtain the TGT using the authentication
  * exchange and added to the Subject's private credentials.
+ * This secret key will be first retrieved from the keytab. If the key
+ * is not available, the user will be prompted for the password. In either
+ * case, the key derived from the password will be added to the
+ * Subject's private credentials set.
  * <ul>
  * <p><code>isInitiator</code> = false
  *</ul>
@@ -856,11 +875,13 @@
     }
 
     private void validateConfiguration() throws LoginException {
-        if (doNotPrompt && !useTicketCache && !useKeyTab)
+        if (doNotPrompt && !useTicketCache && !useKeyTab
+                && !tryFirstPass && !useFirstPass)
             throw new LoginException
                 ("Configuration Error"
                  + " - either doNotPrompt should be "
-                 + " false or useTicketCache/useKeyTab "
+                 + " false or at least one of useTicketCache, "
+                 + " useKeyTab, tryFirstPass and useFirstPass"
                  + " should be true");
         if (ticketCacheName != null && !useTicketCache)
             throw new LoginException
@@ -872,11 +893,12 @@
             throw new LoginException
                 ("Configuration Error - useKeyTab should be set to true "
                  + "to use the keytab" + keyTabName);
-        if (storeKey && doNotPrompt && !useKeyTab)
+        if (storeKey && doNotPrompt && !useKeyTab
+                && !tryFirstPass && !useFirstPass)
             throw new LoginException
-                ("Configuration Error - either doNotPrompt "
-                 + "should be set to false or "
-                 + "useKeyTab must be set to true for storeKey option");
+                ("Configuration Error - either doNotPrompt should be set to "
+                 + " false or at least one of tryFirstPass, useFirstPass "
+                 + "or useKeyTab must be set to true for storeKey option");
         if (renewTGT && !useTicketCache)
             throw new LoginException
                 ("Configuration Error"
--- a/src/share/classes/java/net/HttpURLConnection.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/src/share/classes/java/net/HttpURLConnection.java	Fri Nov 14 09:43:00 2008 +0000
@@ -73,11 +73,24 @@
      * The fixed content-length when using fixed-length streaming mode.
      * A value of <code>-1</code> means fixed-length streaming mode is disabled
      * for output.
+     *
+     * <P> <B>NOTE:</B> {@link #fixedContentLengthLong} is recommended instead
+     * of this field, as it allows larger content lengths to be set.
+     *
      * @since 1.5
      */
     protected int fixedContentLength = -1;
 
     /**
+     * The fixed content-length when using fixed-length streaming mode.
+     * A value of {@code -1} means fixed-length streaming mode is disabled
+     * for output.
+     *
+     * @since 1.7
+     */
+    protected long fixedContentLengthLong = -1;
+
+    /**
      * Returns the key for the <code>n</code><sup>th</sup> header field.
      * Some implementations may treat the <code>0</code><sup>th</sup>
      * header field as special, i.e. as the status line returned by the HTTP
@@ -109,6 +122,9 @@
      * This exception can be queried for the details of the error.
      * <p>
      * This method must be called before the URLConnection is connected.
+     * <p>
+     * <B>NOTE:</B> {@link #setFixedLengthStreamingMode(long)} is recommended
+     * instead of this method as it allows larger content lengths to be set.
      *
      * @param   contentLength The number of bytes which will be written
      *          to the OutputStream.
@@ -135,6 +151,52 @@
         fixedContentLength = contentLength;
     }
 
+    /**
+     * This method is used to enable streaming of a HTTP request body
+     * without internal buffering, when the content length is known in
+     * advance.
+     *
+     * <P> An exception will be thrown if the application attempts to write
+     * more data than the indicated content-length, or if the application
+     * closes the OutputStream before writing the indicated amount.
+     *
+     * <P> When output streaming is enabled, authentication and redirection
+     * cannot be handled automatically. A {@linkplain HttpRetryException} will
+     * be thrown when reading the response if authentication or redirection
+     * are required. This exception can be queried for the details of the
+     * error.
+     *
+     * <P> This method must be called before the URLConnection is connected.
+     *
+     * <P> The content length set by invoking this method takes precedence
+     * over any value set by {@link #setFixedLengthStreamingMode(int)}.
+     *
+     * @param  contentLength
+     *         The number of bytes which will be written to the OutputStream.
+     *
+     * @throws  IllegalStateException
+     *          if URLConnection is already connected or if a different
+     *          streaming mode is already enabled.
+     *
+     * @throws  IllegalArgumentException
+     *          if a content length less than zero is specified.
+     *
+     * @since 1.7
+     */
+    public void setFixedLengthStreamingMode(long contentLength) {
+        if (connected) {
+            throw new IllegalStateException("Already connected");
+        }
+        if (chunkLength != -1) {
+            throw new IllegalStateException(
+                "Chunked encoding streaming mode set");
+        }
+        if (contentLength < 0) {
+            throw new IllegalArgumentException("invalid content length");
+        }
+        fixedContentLengthLong = contentLength;
+    }
+
     /* Default chunk size (including chunk header) if not specified;
      * we want to keep this in sync with the one defined in
      * sun.net.www.http.ChunkedOutputStream
@@ -170,7 +232,7 @@
         if (connected) {
             throw new IllegalStateException ("Can't set streaming mode: already connected");
         }
-        if (fixedContentLength != -1) {
+        if (fixedContentLength != -1 || fixedContentLengthLong != -1) {
             throw new IllegalStateException ("Fixed length streaming mode set");
         }
         chunkLength = chunklen <=0? DEFAULT_CHUNK_SIZE : chunklen;
--- a/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Fri Nov 14 09:43:00 2008 +0000
@@ -435,8 +435,14 @@
             if (streaming()) {
                 if (chunkLength != -1) {
                     requests.set ("Transfer-Encoding", "chunked");
-                } else {
-                    requests.set ("Content-Length", String.valueOf(fixedContentLength));
+                } else { /* fixed content length */
+                    if (fixedContentLengthLong != -1) {
+                        requests.set ("Content-Length",
+                                      String.valueOf(fixedContentLengthLong));
+                    } else if (fixedContentLength != -1) {
+                        requests.set ("Content-Length",
+                                      String.valueOf(fixedContentLength));
+                    }
                 }
             } else if (poster != null) {
                 /* add Content-Length & POST/PUT data */
@@ -871,11 +877,17 @@
             ps = (PrintStream)http.getOutputStream();
             if (streaming()) {
                 if (strOutputStream == null) {
-                    if (fixedContentLength != -1) {
-                        strOutputStream = new StreamingOutputStream (ps, fixedContentLength);
-                    } else if (chunkLength != -1) {
-                        strOutputStream =
-                            new StreamingOutputStream (new ChunkedOutputStream (ps, chunkLength), -1);
+                    if (chunkLength != -1) { /* chunked */
+                         strOutputStream = new StreamingOutputStream(
+                               new ChunkedOutputStream(ps, chunkLength), -1L);
+                    } else { /* must be fixed content length */
+                        long length = 0L;
+                        if (fixedContentLengthLong != -1) {
+                            length = fixedContentLengthLong;
+                        } else if (fixedContentLength != -1) {
+                            length = fixedContentLength;
+                        }
+                        strOutputStream = new StreamingOutputStream(ps, length);
                     }
                 }
                 return strOutputStream;
@@ -895,7 +907,8 @@
     }
 
     private boolean streaming () {
-        return (fixedContentLength != -1) || (chunkLength != -1);
+        return (fixedContentLength != -1) || (fixedContentLengthLong != -1) ||
+               (chunkLength != -1);
     }
 
     /*
@@ -2619,8 +2632,8 @@
 
     class StreamingOutputStream extends FilterOutputStream {
 
-        int expected;
-        int written;
+        long expected;
+        long written;
         boolean closed;
         boolean error;
         IOException errorExcp;
@@ -2631,10 +2644,10 @@
          *    In the 2nd case, we make sure the expected number of
          *    of bytes are actually written
          */
-        StreamingOutputStream (OutputStream os, int expectedLength) {
+        StreamingOutputStream (OutputStream os, long expectedLength) {
             super (os);
             expected = expectedLength;
-            written = 0;
+            written = 0L;
             closed = false;
             error = false;
         }
@@ -2643,7 +2656,7 @@
         public void write (int b) throws IOException {
             checkError();
             written ++;
-            if (expected != -1 && written > expected) {
+            if (expected != -1L && written > expected) {
                 throw new IOException ("too many bytes written");
             }
             out.write (b);
@@ -2658,7 +2671,7 @@
         public void write (byte[] b, int off, int len) throws IOException {
             checkError();
             written += len;
-            if (expected != -1 && written > expected) {
+            if (expected != -1L && written > expected) {
                 out.close ();
                 throw new IOException ("too many bytes written");
             }
@@ -2691,7 +2704,7 @@
                 return;
             }
             closed = true;
-            if (expected != -1) {
+            if (expected != -1L) {
                 /* not chunked */
                 if (written != expected) {
                     error = true;
--- a/src/share/classes/sun/net/www/protocol/https/HttpsURLConnectionImpl.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/src/share/classes/sun/net/www/protocol/https/HttpsURLConnectionImpl.java	Fri Nov 14 09:43:00 2008 +0000
@@ -527,6 +527,10 @@
         delegate.setFixedLengthStreamingMode(contentLength);
     }
 
+    public void setFixedLengthStreamingMode(long contentLength) {
+        delegate.setFixedLengthStreamingMode(contentLength);
+    }
+
     public void setChunkedStreamingMode (int chunklen) {
         delegate.setChunkedStreamingMode(chunklen);
     }
--- a/src/share/classes/sun/security/jgss/GSSContextImpl.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/src/share/classes/sun/security/jgss/GSSContextImpl.java	Fri Nov 14 09:43:00 2008 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2000-2008 Sun Microsystems, Inc.  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
@@ -284,7 +284,8 @@
         ByteArrayOutputStream bos = new ByteArrayOutputStream(100);
         acceptSecContext(new ByteArrayInputStream(inTok, offset, len),
                          bos);
-        return bos.toByteArray();
+        byte[] out = bos.toByteArray();
+        return (out.length == 0) ? null : out;
     }
 
     public void acceptSecContext(InputStream inStream,
--- a/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java	Fri Nov 14 09:43:00 2008 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2005-2008 Sun Microsystems, Inc.  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
@@ -413,13 +413,14 @@
                     // pull out the mechanism token
                     byte[] accept_token = targToken.getResponseToken();
                     if (accept_token == null) {
-                        // return wth failure
-                        throw new GSSException(errorCode, -1,
-                                        "mechansim token from server is null");
+                        if (!isMechContextEstablished()) {
+                            // return with failure
+                            throw new GSSException(errorCode, -1,
+                                    "mechanism token from server is null");
+                        }
+                    } else {
+                        mechToken = GSS_initSecContext(accept_token);
                     }
-
-                    mechToken = GSS_initSecContext(accept_token);
-
                     // verify MIC
                     if (!GSSUtil.useMSInterop()) {
                         byte[] micToken = targToken.getMechListMIC();
@@ -428,7 +429,6 @@
                                 "verification of MIC on MechList Failed!");
                         }
                     }
-
                     if (isMechContextEstablished()) {
                         state = STATE_DONE;
                         retVal = mechToken;
@@ -556,9 +556,6 @@
 
                 // get the token for mechanism
                 byte[] accept_token = GSS_acceptSecContext(mechToken);
-                if (accept_token == null) {
-                    valid = false;
-                }
 
                 // verify MIC
                 if (!GSSUtil.useMSInterop() && valid) {
--- a/src/share/classes/sun/security/ssl/BaseSSLSocketImpl.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/src/share/classes/sun/security/ssl/BaseSSLSocketImpl.java	Fri Nov 14 09:43:00 2008 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2002-2008 Sun Microsystems, Inc.  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
@@ -256,10 +256,12 @@
                 // ignore
             }
         } finally {
-            // we call close on the underlying socket anyway, but be
-            // doubly sure all resources get released.
-            // note that we don't need to worry about self, the GC
-            // will finalize that separately
+            // We called close on the underlying socket above to
+            // make doubly sure all resources got released.  We
+            // don't finalize self in the case of overlain sockets,
+            // that's a different object which the GC will finalize
+            // separately.
+
             super.finalize();
         }
     }
--- a/src/share/classes/sun/security/ssl/HelloExtensions.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/src/share/classes/sun/security/ssl/HelloExtensions.java	Fri Nov 14 09:43:00 2008 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006-2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2006-2008 Sun Microsystems, Inc.  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
@@ -218,7 +218,10 @@
             throws IOException {
         super(type);
         data = new byte[len];
-        s.read(data);
+        // s.read() does not handle 0-length arrays.
+        if (len != 0) {
+            s.read(data);
+        }
     }
 
     int length() {
--- a/src/share/classes/sun/security/ssl/SSLServerSocketImpl.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/src/share/classes/sun/security/ssl/SSLServerSocketImpl.java	Fri Nov 14 09:43:00 2008 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright 1996-2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc.  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
@@ -304,14 +304,18 @@
                          enabledCipherSuites, doClientAuth,
                          enableSessionCreation, enabledProtocols);
 
-            ServerHandshaker handshaker = tmp.getServerHandshaker();
+            try {
+                ServerHandshaker handshaker = tmp.getServerHandshaker();
 
-            for (Iterator t = enabledCipherSuites.iterator(); t.hasNext(); ) {
-                CipherSuite suite = (CipherSuite)t.next();
-                if (handshaker.trySetCipherSuite(suite)) {
-                    checkedEnabled = true;
-                    return;
+                for (Iterator t = enabledCipherSuites.iterator(); t.hasNext(); ) {
+                    CipherSuite suite = (CipherSuite)t.next();
+                    if (handshaker.trySetCipherSuite(suite)) {
+                        checkedEnabled = true;
+                        return;
+                    }
                 }
+            } finally {
+                tmp.closeSocket();
             }
 
             //
--- a/src/share/classes/sun/security/ssl/SSLSocketImpl.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/src/share/classes/sun/security/ssl/SSLSocketImpl.java	Fri Nov 14 09:43:00 2008 +0000
@@ -1012,6 +1012,22 @@
      */
     ServerHandshaker getServerHandshaker() throws SSLException {
         initHandshaker();
+
+         // The connection state would have been set to cs_HANDSHAKE during the
+         // handshaking initializing, however the caller may not have the
+         // the low level connection's established, which is not consistent with
+         // the HANDSHAKE state. As if it is unconnected, we need to reset the
+         // connection state to cs_START.
+         if (!isConnected()) {
+             connectionState = cs_START;
+         }
+
+         // Make sure that we get a ServerHandshaker.
+         // This should never happen.
+         if (!(handshaker instanceof ServerHandshaker)) {
+             throw new SSLProtocolException("unexpected handshaker instance");
+         }
+
         return (ServerHandshaker)handshaker;
     }
 
@@ -1273,7 +1289,8 @@
         }
     }
 
-    private void closeSocket() throws IOException {
+    protected void closeSocket() throws IOException {
+
         if ((debug != null) && Debug.isOn("ssl")) {
             System.out.println(threadName() + ", called closeSocket()");
         }
--- a/src/share/native/java/util/zip/zip_util.c	Mon Nov 10 12:28:47 2008 +0000
+++ b/src/share/native/java/util/zip/zip_util.c	Fri Nov 14 09:43:00 2008 +0000
@@ -273,8 +273,8 @@
 /*
  * Searches for end of central directory (END) header. The contents of
  * the END header will be read and placed in endbuf. Returns the file
- * position of the END header, otherwise returns 0 if the END header
- * was not found or -1 if an error occurred.
+ * position of the END header, otherwise returns -1 if the END header
+ * was not found or an error occurred.
  */
 static jlong
 findEND(jzfile *zip, void *endbuf)
@@ -314,7 +314,7 @@
             }
         }
     }
-    return 0; /* END header not found */
+    return -1; /* END header not found */
 }
 
 /*
@@ -460,9 +460,8 @@
 
 /*
  * Reads zip file central directory. Returns the file position of first
- * CEN header, otherwise returns 0 if central directory not found or -1
- * if an error occurred. If zip->msg != NULL then the error was a zip
- * format error and zip->msg has the error text.
+ * CEN header, otherwise returns -1 if an error occured. If zip->msg != NULL
+ * then the error was a zip format error and zip->msg has the error text.
  * Always pass in -1 for knownTotal; it's used for a recursive call.
  */
 static jlong
@@ -488,9 +487,9 @@
 
     /* Get position of END header */
     if ((endpos = findEND(zip, endbuf)) == -1)
-        return -1; /* system error */
+        return -1; /* no END header or system error */
 
-    if (endpos == 0) return 0;  /* END header not found */
+    if (endpos == 0) return 0;  /* only END header present */
 
     freeCEN(zip);
 
--- a/test/com/sun/net/httpserver/bugs/FixedLengthInputStream.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/test/com/sun/net/httpserver/bugs/FixedLengthInputStream.java	Fri Nov 14 09:43:00 2008 +0000
@@ -23,7 +23,7 @@
 
 /**
  * @test
- * @bug 6756771
+ * @bug 6756771 6755625
  * @summary  com.sun.net.httpserver.HttpServer should handle POSTs larger than 2Gig
  */
 
@@ -44,34 +44,16 @@
 {
     static final long POST_SIZE = 4L * 1024L * 1024L * 1024L; // 4Gig
 
-    /* Remove when CR 6755625 is fixed */
-    static final String requestHeaders =  ((new StringBuilder())
-        .append("POST /flis/ HTTP/1.1\r\n")
-        .append("User-Agent: Java/1.7.0\r\n")
-        .append("Host: localhost\r\n")
-        .append("Accept: text/html, image/gif, image/jpeg,")
-        .append(        " *; q=.2, */*; q=.2\r\n")
-        .append("Content-Length: 4294967296\r\n\r\n")).toString();
-
     void test(String[] args) throws IOException {
         HttpServer httpServer = startHttpServer();
         int port = httpServer.getAddress().getPort();
         try {
-          /* Uncomment & when CR 6755625 is fixed, remove socket code
             URL url = new URL("http://localhost:" + port + "/flis/");
             HttpURLConnection uc = (HttpURLConnection)url.openConnection();
             uc.setDoOutput(true);
             uc.setRequestMethod("POST");
             uc.setFixedLengthStreamingMode(POST_SIZE);
             OutputStream os = uc.getOutputStream();
-          */
-
-            Socket socket = new Socket("localhost", port);
-            OutputStream os = socket.getOutputStream();
-            PrintStream ps = new PrintStream(os);
-            debug("Request: " + requestHeaders);
-            ps.print(requestHeaders);
-            ps.flush();
 
             /* create a 32K byte array with data to POST */
             int thirtyTwoK = 32 * 1024;
@@ -84,18 +66,12 @@
                 os.write(ba);
             }
 
-          /* Uncomment & when CR 6755625 is fixed, remove socket code
             os.close();
             InputStream is = uc.getInputStream();
             while(is.read(ba) != -1);
             is.close();
-           */
 
-           InputStream is = socket.getInputStream();
-           is.read();
-           socket.close();
-
-           pass();
+            pass();
         } finally {
             httpServer.stop(0);
         }
--- a/test/java/util/zip/TestEmptyZip.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/test/java/util/zip/TestEmptyZip.java	Fri Nov 14 09:43:00 2008 +0000
@@ -39,35 +39,24 @@
             throw new Exception("failed to delete " + zipName);
         }
 
-        // Verify 0-length file cannot be read
         f.createNewFile();
-        ZipFile zf = null;
         try {
-            zf = new ZipFile(f);
-            fail();
-        } catch (Exception ex) {
-            check(ex.getMessage().contains("zip file is empty"));
+            // Verify 0-length file cannot be read
+            checkCannotRead(f);
+
+            // Verify non-zip file cannot be read
+            OutputStream out = new FileOutputStream(f);
+            try {
+                out.write("class Foo { }".getBytes());
+            } finally {
+                out.close();
+            }
+            checkCannotRead(f);
+
         } finally {
-            if (zf != null) {
-                zf.close();
-            }
+            f.delete();
         }
 
-        ZipInputStream zis = null;
-        try {
-            zis = new ZipInputStream(new FileInputStream(f));
-            ZipEntry ze = zis.getNextEntry();
-            check(ze == null);
-        } catch (Exception ex) {
-            unexpected(ex);
-        } finally {
-            if (zis != null) {
-                zis.close();
-            }
-        }
-
-        f.delete();
-
         // Verify 0-entries file can be written
         write(f);
 
@@ -78,6 +67,29 @@
         f.delete();
     }
 
+    static void checkCannotRead(File f) throws IOException {
+        try {
+            new ZipFile(f).close();
+            fail();
+        } catch (ZipException ze) {
+            if (f.length() == 0) {
+                check(ze.getMessage().contains("zip file is empty"));
+            } else {
+                pass();
+            }
+        }
+        ZipInputStream zis = null;
+        try {
+            zis = new ZipInputStream(new FileInputStream(f));
+            ZipEntry ze = zis.getNextEntry();
+            check(ze == null);
+        } catch (IOException ex) {
+            unexpected(ex);
+        } finally {
+            if (zis != null) zis.close();
+        }
+    }
+
     static void write(File f) throws Exception {
         ZipOutputStream zos = null;
         try {
--- a/test/sun/security/krb5/auto/Context.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/test/sun/security/krb5/auto/Context.java	Fri Nov 14 09:43:00 2008 +0000
@@ -109,13 +109,22 @@
         out.s = new Subject();
         Krb5LoginModule krb5 = new Krb5LoginModule();
         Map<String, String> map = new HashMap<String, String>();
-        map.put("tryFirstPass", "true");
+        Map<String, Object> shared = new HashMap<String, Object>();
+
+        if (pass != null) {
+            map.put("useFirstPass", "true");
+            shared.put("javax.security.auth.login.name", user);
+            shared.put("javax.security.auth.login.password", pass);
+        } else {
+            map.put("doNotPrompt", "true");
+            map.put("useTicketCache", "true");
+            if (user != null) {
+                map.put("principal", user);
+            }
+        }
         if (storeKey) {
             map.put("storeKey", "true");
         }
-        Map<String, Object> shared = new HashMap<String, Object>();
-        shared.put("javax.security.auth.login.name", user);
-        shared.put("javax.security.auth.login.password", pass);
 
         krb5.initialize(out.s, null, shared, map);
         krb5.login();
@@ -360,6 +369,10 @@
                     if (me.x.isEstablished()) {
                         me.f = true;
                         System.out.println(c.name + " side established");
+                        if (input != null) {
+                            throw new Exception("Context established but " +
+                                    "still receive token at " + c.name);
+                        }
                         return null;
                     } else {
                         System.out.println(c.name + " call initSecContext");
@@ -374,6 +387,10 @@
                     if (me.x.isEstablished()) {
                         me.f = true;
                         System.out.println(s.name + " side established");
+                        if (input != null) {
+                            throw new Exception("Context established but " +
+                                    "still receive token at " + s.name);
+                        }
                         return null;
                     } else {
                         System.out.println(s.name + " called acceptSecContext");
--- a/test/sun/security/krb5/auto/KDC.java	Mon Nov 10 12:28:47 2008 +0000
+++ b/test/sun/security/krb5/auto/KDC.java	Fri Nov 14 09:43:00 2008 +0000
@@ -32,6 +32,7 @@
 import java.util.concurrent.*;
 import sun.security.krb5.*;
 import sun.security.krb5.internal.*;
+import sun.security.krb5.internal.ccache.CredentialsCache;
 import sun.security.krb5.internal.crypto.KeyUsage;
 import sun.security.krb5.internal.ktab.KeyTab;
 import sun.security.util.DerInputStream;
@@ -765,7 +766,29 @@
             DerOutputStream out = new DerOutputStream();
             out.write(DerValue.createTag(DerValue.TAG_APPLICATION,
                     true, (byte)Krb5.KRB_AS_REP), asRep.asn1Encode());
-            return out.toByteArray();
+            byte[] result = out.toByteArray();
+
+            // Added feature:
+            // Write the current issuing TGT into a ccache file specified
+            // by the system property below.
+            String ccache = System.getProperty("test.kdc.save.ccache");
+            if (ccache != null) {
+                asRep.encKDCRepPart = enc_part;
+                sun.security.krb5.internal.ccache.Credentials credentials =
+                    new sun.security.krb5.internal.ccache.Credentials(asRep);
+                asReq.reqBody.cname.setRealm(getRealm());
+                CredentialsCache cache =
+                    CredentialsCache.create(asReq.reqBody.cname, ccache);
+                if (cache == null) {
+                   throw new IOException("Unable to create the cache file " +
+                                         ccache);
+                }
+                cache.update(credentials);
+                cache.save();
+                new File(ccache).deleteOnExit();
+            }
+
+            return result;
         } catch (KrbException ke) {
             ke.printStackTrace(System.out);
             KRBError kerr = ke.getError();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/krb5/auto/LoginModuleOptions.java	Fri Nov 14 09:43:00 2008 +0000
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6765491
+ * @summary Krb5LoginModule a little too restrictive, and the doc is not clear.
+ */
+import com.sun.security.auth.module.Krb5LoginModule;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+public class LoginModuleOptions {
+
+    private static final String NAME = "javax.security.auth.login.name";
+    private static final String PWD = "javax.security.auth.login.password";
+
+    public static void main(String[] args) throws Exception {
+        OneKDC kdc = new OneKDC(null);
+        kdc.addPrincipal("foo", "bar".toCharArray());
+        kdc.writeKtab(OneKDC.KTAB); // rewrite to add foo
+
+        // All 4 works: keytab, shared state, callback, cache
+        login(null, "useKeyTab", "true", "principal", "dummy");
+        login(null, "tryFirstPass", "true", NAME, OneKDC.USER,
+                PWD, OneKDC.PASS);
+        System.setProperty("test.kdc.save.ccache", "krbcc");
+        login(new MyCallback(OneKDC.USER, OneKDC.PASS));    // save the cache
+        System.clearProperty("test.kdc.save.ccache");
+        login(null, "useTicketCache", "true", "ticketCache", "krbcc");
+
+        // Fallbacks
+        // 1. ccache -> keytab
+        login(null, "useTicketCache", "true", "ticketCache", "krbcc_non_exists",
+                "useKeyTab", "true", "principal", "dummy");
+        // 2. keytab -> shared
+        login(null, "useKeyTab", "true", "principal", "dummy",
+                "keyTab", "ktab_non_exist",
+                "tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
+        // 3. shared -> callback
+        // 3.1. useFirstPass, no callback
+        boolean failed = false;
+        try {
+            login(new MyCallback(OneKDC.USER, OneKDC.PASS),
+                    "useFirstPass", "true",
+                    NAME, OneKDC.USER, PWD, "haha".toCharArray());
+        } catch (Exception e) {
+            failed = true;
+        }
+        if (!failed) {
+            throw new Exception("useFirstPass should not fallback to callback");
+        }
+        // 3.2. tryFirstPass, has callback
+        login(new MyCallback(OneKDC.USER, OneKDC.PASS),
+                "tryFirstPass", "true",
+                NAME, OneKDC.USER, PWD, "haha".toCharArray());
+
+        // Preferences of type
+        // 1. ccache preferred to keytab
+        login(new MyCallback("foo", null),
+                "useTicketCache", "true", "ticketCache", "krbcc",
+                "useKeyTab", "true");
+        // 2. keytab preferred to shared. This test case is not exactly correct,
+        // because principal=dummy would shadow the PWD setting in the shared
+        // state. So by only looking at the final authentication user name
+        // (which is how this program does), there's no way to tell if keyTab
+        // is picked first, or shared is tried first but fallback to keytab.
+        login(null, "useKeyTab", "true", "principal", "dummy",
+                "tryFirstPass", "true", NAME, "foo", PWD, "bar".toCharArray());
+        // 3. shared preferred to callback
+        login(new MyCallback("foo", "bar".toCharArray()),
+                "tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
+
+        // Preferences of username
+        // 1. principal preferred to NAME (NAME can be wrong or missing)
+        login(null, "principal", OneKDC.USER,
+                "tryFirstPass", "true", NAME, "someone_else", PWD, OneKDC.PASS);
+        login(null, "principal", OneKDC.USER,
+                "tryFirstPass", "true", PWD, OneKDC.PASS);
+        // 2. NAME preferred to callback
+        login(new MyCallback("someone_else", OneKDC.PASS),
+                "principal", OneKDC.USER);
+        // 3. With tryFirstPass, NAME preferred to callback
+        login(new MyCallback("someone_else", null),
+                "tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
+        // 3.1. you must provide a NAME (when there's no principal)
+        failed = false;
+        try {
+            login(new MyCallback(OneKDC.USER, null),
+                    "tryFirstPass", "true", PWD, OneKDC.PASS);
+        } catch (Exception e) {
+            failed = true;
+        }
+        if (!failed) {
+            throw new Exception("useFirstPass must provide a NAME");
+        }
+        // 3.2 Hybrid, you can use NAME as "", and provide it using callback.
+        // I don't think this is designed.
+        login(new MyCallback(OneKDC.USER, null),
+                "tryFirstPass", "true", NAME, "", PWD, OneKDC.PASS);
+
+        // Test for the bug fix: doNotPrompt can be true if tryFirstPass=true
+        login(null, "doNotPrompt", "true", "storeKey", "true",
+                "tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
+    }
+
+    static void login(CallbackHandler callback, Object... options)
+            throws Exception {
+        Krb5LoginModule krb5 = new Krb5LoginModule();
+        Subject subject = new Subject();
+        Map<String, String> map = new HashMap<String, String>();
+        Map<String, Object> shared = new HashMap<String, Object>();
+
+        int count = options.length / 2;
+        for (int i = 0; i < count; i++) {
+            String key = (String) options[2 * i];
+            Object value = options[2 * i + 1];
+            if (key.startsWith("javax")) {
+                shared.put(key, value);
+            } else {
+                map.put(key, (String) value);
+            }
+        }
+        krb5.initialize(subject, callback, shared, map);
+        krb5.login();
+        krb5.commit();
+        if (!subject.getPrincipals().iterator().next()
+                .getName().startsWith(OneKDC.USER)) {
+            throw new Exception("The authenticated is not " + OneKDC.USER);
+        }
+    }
+
+    static class MyCallback implements CallbackHandler {
+
+        private String name;
+        private char[] password;
+
+        public MyCallback(String name, char[] password) {
+            this.name = name;
+            this.password = password;
+        }
+
+        public void handle(Callback[] callbacks) {
+            for (Callback callback : callbacks) {
+                System.err.println(callback);
+                if (callback instanceof NameCallback) {
+                    System.err.println("name is " + name);
+                    ((NameCallback) callback).setName(name);
+                }
+                if (callback instanceof PasswordCallback) {
+                    System.err.println("pass is " + new String(password));
+                    ((PasswordCallback) callback).setPassword(password);
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/krb5/auto/NonMutualSpnego.java	Fri Nov 14 09:43:00 2008 +0000
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6733095
+ * @summary Failure when SPNEGO request non-Mutual
+ */
+
+import sun.security.jgss.GSSUtil;
+
+public class NonMutualSpnego {
+
+    public static void main(String[] args)
+            throws Exception {
+
+        // Create and start the KDC
+        new OneKDC(null).writeJAASConf();
+        new NonMutualSpnego().go();
+    }
+
+    void go() throws Exception {
+        Context c = Context.fromJAAS("client");
+        Context s = Context.fromJAAS("server");
+
+        c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_SPNEGO_MECH_OID);
+        c.x().requestMutualAuth(false);
+        s.startAsServer(GSSUtil.GSS_SPNEGO_MECH_OID);
+
+        Context.handshake(c, s);
+
+        Context.transmit("i say high --", c, s);
+        Context.transmit("   you say low", s, c);
+
+        c.dispose();
+        s.dispose();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLEngineImpl/EmptyExtensionData.java	Fri Nov 14 09:43:00 2008 +0000
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6728126
+ * @summary Parsing Extensions in Client Hello message is done in a wrong way
+ */
+
+import javax.net.ssl.*;
+import javax.net.ssl.SSLEngineResult.*;
+import java.io.*;
+import java.security.*;
+import java.nio.*;
+
+public class EmptyExtensionData {
+
+    private static boolean debug = false;
+
+    private static String pathToStores = "../../../../../../../etc";
+    private static String keyStoreFile = "keystore";
+    private static String trustStoreFile = "truststore";
+    private static String passwd = "passphrase";
+
+    private static String keyFilename =
+            System.getProperty("test.src", "./") + "/" + pathToStores +
+                "/" + keyStoreFile;
+    private static String trustFilename =
+            System.getProperty("test.src", "./") + "/" + pathToStores +
+                "/" + trustStoreFile;
+
+    private static void checkDone(SSLEngine ssle) throws Exception {
+        if (!ssle.isInboundDone()) {
+            throw new Exception("isInboundDone isn't done");
+        }
+        if (!ssle.isOutboundDone()) {
+            throw new Exception("isOutboundDone isn't done");
+        }
+    }
+
+    private static void runTest(SSLEngine ssle) throws Exception {
+        // a client hello message with an empty extension data
+        byte[] msg_clihello = {
+                (byte)0x16, (byte)0x03, (byte)0x01, (byte)0x00,
+                (byte)0x6f, (byte)0x01, (byte)0x00, (byte)0x00,
+                (byte)0x6b, (byte)0x03, (byte)0x01, (byte)0x48,
+                (byte)0x90, (byte)0x71, (byte)0xfc, (byte)0xf9,
+                (byte)0xa2, (byte)0x3a, (byte)0xd7, (byte)0xa8,
+                (byte)0x0b, (byte)0x25, (byte)0xf1, (byte)0x2b,
+                (byte)0x88, (byte)0x80, (byte)0x66, (byte)0xca,
+                (byte)0x07, (byte)0x78, (byte)0x2a, (byte)0x08,
+                (byte)0x9d, (byte)0x62, (byte)0x1d, (byte)0x89,
+                (byte)0xc9, (byte)0x1e, (byte)0x1f, (byte)0xe5,
+                (byte)0x92, (byte)0xfe, (byte)0x8d, (byte)0x00,
+                (byte)0x00, (byte)0x24, (byte)0x00, (byte)0x88,
+                (byte)0x00, (byte)0x87, (byte)0x00, (byte)0x39,
+                (byte)0x00, (byte)0x38, (byte)0x00, (byte)0x84,
+                (byte)0x00, (byte)0x35, (byte)0x00, (byte)0x45,
+                (byte)0x00, (byte)0x44, (byte)0x00, (byte)0x33,
+                (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x41,
+                (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x05,
+                (byte)0x00, (byte)0x2f, (byte)0x00, (byte)0x16,
+                (byte)0x00, (byte)0x13, (byte)0xfe, (byte)0xff,
+                (byte)0x00, (byte)0x0a, (byte)0x01, (byte)0x00,
+                (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x00,
+                (byte)0x00, (byte)0x16, (byte)0x00, (byte)0x14,
+                (byte)0x00, (byte)0x00, (byte)0x11, (byte)0x6a,
+                (byte)0x75, (byte)0x73, (byte)0x74, (byte)0x69,
+                (byte)0x6e, (byte)0x2e, (byte)0x75, (byte)0x6b,
+                (byte)0x2e, (byte)0x73, (byte)0x75, (byte)0x6e,
+                (byte)0x2e, (byte)0x63, (byte)0x6f, (byte)0x6d,
+                (byte)0x00, (byte)0x23, (byte)0x00, (byte)0x00
+            };
+        ByteBuffer bf_clihello = ByteBuffer.wrap(msg_clihello);
+
+        SSLSession session = ssle.getSession();
+        int appBufferMax = session.getApplicationBufferSize();
+        int netBufferMax = session.getPacketBufferSize();
+
+        ByteBuffer serverIn = ByteBuffer.allocate(appBufferMax + 50);
+        ByteBuffer serverOut = ByteBuffer.wrap("I'm Server".getBytes());
+        ByteBuffer sTOc = ByteBuffer.allocate(netBufferMax);
+
+        ssle.beginHandshake();
+
+        // unwrap the clientHello message.
+        SSLEngineResult result = ssle.unwrap(bf_clihello, serverIn);
+        System.out.println("server unwrap " + result);
+        runDelegatedTasks(result, ssle);
+
+        // one more step, ensure the clientHello message is parsed.
+        SSLEngineResult.HandshakeStatus status = ssle.getHandshakeStatus();
+        if ( status == HandshakeStatus.NEED_UNWRAP) {
+            result = ssle.unwrap(bf_clihello, serverIn);
+            System.out.println("server unwrap " + result);
+            runDelegatedTasks(result, ssle);
+        } else if ( status == HandshakeStatus.NEED_WRAP) {
+            result = ssle.wrap(serverOut, sTOc);
+            System.out.println("server wrap " + result);
+            runDelegatedTasks(result, ssle);
+        } else {
+            throw new Exception("unexpected handshake status " + status);
+        }
+
+        // enough, stop
+    }
+
+    /*
+     * If the result indicates that we have outstanding tasks to do,
+     * go ahead and run them in this thread.
+     */
+    private static void runDelegatedTasks(SSLEngineResult result,
+            SSLEngine engine) throws Exception {
+
+        if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+            Runnable runnable;
+            while ((runnable = engine.getDelegatedTask()) != null) {
+                log("\trunning delegated task...");
+                runnable.run();
+            }
+            HandshakeStatus hsStatus = engine.getHandshakeStatus();
+            if (hsStatus == HandshakeStatus.NEED_TASK) {
+                throw new Exception(
+                    "handshake shouldn't need additional tasks");
+            }
+            log("\tnew HandshakeStatus: " + hsStatus);
+        }
+    }
+
+    public static void main(String args[]) throws Exception {
+
+        SSLEngine ssle = createSSLEngine(keyFilename, trustFilename);
+        runTest(ssle);
+
+        System.out.println("Test Passed.");
+    }
+
+    /*
+     * Create an initialized SSLContext to use for this test.
+     */
+    static private SSLEngine createSSLEngine(String keyFile, String trustFile)
+            throws Exception {
+
+        SSLEngine ssle;
+
+        KeyStore ks = KeyStore.getInstance("JKS");
+        KeyStore ts = KeyStore.getInstance("JKS");
+
+        char[] passphrase = "passphrase".toCharArray();
+
+        ks.load(new FileInputStream(keyFile), passphrase);
+        ts.load(new FileInputStream(trustFile), passphrase);
+
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+        kmf.init(ks, passphrase);
+
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+        tmf.init(ts);
+
+        SSLContext sslCtx = SSLContext.getInstance("TLS");
+
+        sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+        ssle = sslCtx.createSSLEngine();
+        ssle.setUseClientMode(false);
+
+        return ssle;
+    }
+
+
+    private static void log(String str) {
+        if (debug) {
+            System.out.println(str);
+        }
+    }
+}