--- a/jdk/make/mapfiles/libjava/mapfile-vers Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/make/mapfiles/libjava/mapfile-vers Thu Jun 04 18:49:37 2015 -0700
@@ -166,6 +166,16 @@
Java_java_lang_Package_getSystemPackage0;
Java_java_lang_Package_getSystemPackages0;
Java_java_lang_ProcessEnvironment_environ;
+ Java_java_lang_ProcessHandleImpl_getCurrentPid0;
+ Java_java_lang_ProcessHandleImpl_parent0;
+ Java_java_lang_ProcessHandleImpl_isAlive0;
+ Java_java_lang_ProcessHandleImpl_getProcessPids0;
+ Java_java_lang_ProcessHandleImpl_destroy0;
+ Java_java_lang_ProcessHandleImpl_waitForProcessExit0;
+ Java_java_lang_ProcessHandleImpl_00024Info_initIDs;
+ Java_java_lang_ProcessHandleImpl_00024Info_info0;
+ Java_java_lang_ProcessImpl_init;
+ Java_java_lang_ProcessImpl_forkAndExec;
Java_java_lang_reflect_Array_get;
Java_java_lang_reflect_Array_getBoolean;
Java_java_lang_reflect_Array_getByte;
@@ -214,10 +224,6 @@
Java_java_lang_Throwable_fillInStackTrace;
Java_java_lang_Throwable_getStackTraceDepth;
Java_java_lang_Throwable_getStackTraceElement;
- Java_java_lang_ProcessImpl_init;
- Java_java_lang_ProcessImpl_waitForProcessExit;
- Java_java_lang_ProcessImpl_forkAndExec;
- Java_java_lang_ProcessImpl_destroyProcess;
Java_java_nio_Bits_copyFromShortArray;
Java_java_nio_Bits_copyToShortArray;
Java_java_nio_Bits_copyFromIntArray;
@@ -277,7 +283,7 @@
Java_jdk_internal_jimage_concurrent_ConcurrentPReader_initIDs;
Java_jdk_internal_jimage_concurrent_ConcurrentPReader_pread;
-
+
# ZipFile.c needs this one
throwFileNotFoundException;
# zip_util.c needs this one
--- a/jdk/make/mapfiles/libnet/mapfile-vers Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/make/mapfiles/libnet/mapfile-vers Thu Jun 04 18:49:37 2015 -0700
@@ -42,7 +42,7 @@
Java_java_net_Inet4Address_init;
Java_java_net_Inet6Address_init;
Java_java_net_PlainDatagramSocketImpl_setTTL;
- Java_java_net_PlainDatagramSocketImpl_socketSetOption;
+ Java_java_net_PlainDatagramSocketImpl_socketSetOption0;
Java_java_net_PlainDatagramSocketImpl_bind0;
Java_java_net_PlainSocketImpl_socketAccept;
Java_java_net_DatagramPacket_init;
@@ -73,7 +73,7 @@
Java_java_net_SocketOutputStream_init;
Java_java_net_PlainDatagramSocketImpl_peek;
Java_java_net_PlainDatagramSocketImpl_peekData;
- Java_java_net_PlainSocketImpl_socketSetOption;
+ Java_java_net_PlainSocketImpl_socketSetOption0;
Java_java_net_PlainSocketImpl_socketSendUrgentData;
Java_java_net_PlainDatagramSocketImpl_datagramSocketCreate;
Java_java_net_PlainSocketImpl_socketGetOption;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "java_lang_ProcessHandleImpl.h"
+#include "java_lang_ProcessHandleImpl_Info.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/sysctl.h>
+
+/**
+ * Implementations of ProcessHandleImpl functions for MAC OS X;
+ * are NOT common to all Unix variants.
+ */
+
+static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid);
+static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid);
+
+/*
+ * Common Unix function to lookup the uid and return the user name.
+ */
+extern jstring uidToUser(JNIEnv* env, uid_t uid);
+
+/* Field id for jString 'command' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_commandID;
+
+/* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_argumentsID;
+
+/* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_totalTimeID;
+
+/* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_startTimeID;
+
+/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_userID;
+
+/* static value for clock ticks per second. */
+static long clock_ticks_per_second;
+
+/**************************************************************
+ * Static method to initialize field IDs and the ticks per second rate.
+ *
+ * Class: java_lang_ProcessHandleImpl_Info
+ * Method: initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs
+ (JNIEnv *env, jclass clazz) {
+
+ CHECK_NULL(ProcessHandleImpl_Info_commandID =
+ (*env)->GetFieldID(env, clazz, "command", "Ljava/lang/String;"));
+ CHECK_NULL(ProcessHandleImpl_Info_argumentsID =
+ (*env)->GetFieldID(env, clazz, "arguments", "[Ljava/lang/String;"));
+ CHECK_NULL(ProcessHandleImpl_Info_totalTimeID =
+ (*env)->GetFieldID(env, clazz, "totalTime", "J"));
+ CHECK_NULL(ProcessHandleImpl_Info_startTimeID =
+ (*env)->GetFieldID(env, clazz, "startTime", "J"));
+ CHECK_NULL(ProcessHandleImpl_Info_userID =
+ (*env)->GetFieldID(env, clazz, "user", "Ljava/lang/String;"));
+ clock_ticks_per_second = sysconf(_SC_CLK_TCK);
+}
+
+/*
+ * Returns the parent pid of the requested pid.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: parent0
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0
+(JNIEnv *env, jobject obj, jlong jpid) {
+ pid_t pid = (pid_t) jpid;
+ pid_t ppid = -1;
+
+ if (pid == getpid()) {
+ ppid = getppid();
+ } else {
+ const pid_t pid = (pid_t) jpid;
+ struct kinfo_proc kp;
+ size_t bufSize = sizeof kp;
+
+ // Read the process info for the specific pid
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+ if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
+ JNU_ThrowByNameWithLastError(env,
+ "java/lang/RuntimeException", "sysctl failed");
+ return -1;
+ }
+ ppid = (bufSize > 0 && kp.kp_proc.p_pid == pid) ? kp.kp_eproc.e_ppid : -1;
+ }
+ return (jlong) ppid;
+}
+
+/*
+ * Returns the children of the requested pid and optionally each parent.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: getProcessPids0
+ * Signature: (J[J[J)I
+ *
+ * Use sysctl to accumulate any process whose parent pid is zero or matches.
+ * The resulting pids are stored into the array of longs.
+ * The number of pids is returned if they all fit.
+ * If the parentArray is non-null, store the parent pid.
+ * If the array is too short, excess pids are not stored and
+ * the desired length is returned.
+ */
+JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0
+(JNIEnv *env, jclass clazz, jlong jpid,
+ jlongArray jarray, jlongArray jparentArray)
+{
+ size_t count = 0;
+ jlong* pids = NULL;
+ jlong* ppids = NULL;
+ size_t parentArraySize = 0;
+ size_t arraySize = 0;
+ size_t bufSize = 0;
+ pid_t pid = (pid_t) jpid;
+
+ arraySize = (*env)->GetArrayLength(env, jarray);
+ JNU_CHECK_EXCEPTION_RETURN(env, -1);
+ if (jparentArray != NULL) {
+ parentArraySize = (*env)->GetArrayLength(env, jparentArray);
+ JNU_CHECK_EXCEPTION_RETURN(env, -1);
+
+ if (arraySize != parentArraySize) {
+ JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
+ return 0;
+ }
+ }
+
+ // Get buffer size needed to read all processes
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
+ if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0) {
+ JNU_ThrowByNameWithLastError(env,
+ "java/lang/RuntimeException", "sysctl failed");
+ return -1;
+ }
+
+ // Allocate buffer big enough for all processes
+ void *buffer = malloc(bufSize);
+ if (buffer == NULL) {
+ JNU_ThrowOutOfMemoryError(env, "malloc failed");
+ return -1;
+ }
+
+ // Read process info for all processes
+ if (sysctl(mib, 4, buffer, &bufSize, NULL, 0) < 0) {
+ JNU_ThrowByNameWithLastError(env,
+ "java/lang/RuntimeException", "sysctl failed");
+ free(buffer);
+ return -1;
+ }
+
+ do { // Block to break out of on Exception
+ struct kinfo_proc *kp = (struct kinfo_proc *) buffer;
+ unsigned long nentries = bufSize / sizeof (struct kinfo_proc);
+ long i;
+
+ pids = (*env)->GetLongArrayElements(env, jarray, NULL);
+ if (pids == NULL) {
+ break;
+ }
+ if (jparentArray != NULL) {
+ ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL);
+ if (ppids == NULL) {
+ break;
+ }
+ }
+
+ // Process each entry in the buffer
+ for (i = nentries; --i >= 0; ++kp) {
+ if (pid == 0 || kp->kp_eproc.e_ppid == pid) {
+ if (count < arraySize) {
+ // Only store if it fits
+ pids[count] = (jlong) kp->kp_proc.p_pid;
+ if (ppids != NULL) {
+ // Store the parentPid
+ ppids[count] = (jlong) kp->kp_eproc.e_ppid;
+ }
+ }
+ count++; // Count to tabulate size needed
+ }
+ }
+ } while (0);
+
+ if (pids != NULL) {
+ (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
+ }
+ if (ppids != NULL) {
+ (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
+ }
+
+ free(buffer);
+ // If more pids than array had size for; count will be greater than array size
+ return count;
+}
+
+/**************************************************************
+ * Implementation of ProcessHandleImpl_Info native methods.
+ */
+
+/*
+ * Fill in the Info object from the OS information about the process.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: info0
+ * Signature: (J)I
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0
+ (JNIEnv *env, jobject jinfo, jlong jpid) {
+ pid_t pid = (pid_t) jpid;
+ getStatInfo(env, jinfo, pid);
+ getCmdlineInfo(env, jinfo, pid);
+}
+
+/**
+ * Read /proc/<pid>/stat and fill in the fields of the Info object.
+ * The executable name, plus the user, system, and start times are gathered.
+ */
+static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t jpid) {
+ jlong totalTime; // nanoseconds
+ unsigned long long startTime; // microseconds
+
+ const pid_t pid = (pid_t) jpid;
+ struct kinfo_proc kp;
+ size_t bufSize = sizeof kp;
+
+ // Read the process info for the specific pid
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+
+ if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
+ if (errno == EINVAL) {
+ return;
+ } else {
+ JNU_ThrowByNameWithLastError(env,
+ "java/lang/RuntimeException", "sysctl failed");
+ }
+ return;
+ }
+
+ // Convert the UID to the username
+ jstring name = NULL;
+ CHECK_NULL((name = uidToUser(env, kp.kp_eproc.e_ucred.cr_uid)));
+ (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name);
+ JNU_CHECK_EXCEPTION(env);
+
+ startTime = kp.kp_proc.p_starttime.tv_sec * 1000 +
+ kp.kp_proc.p_starttime.tv_usec / 1000;
+
+ (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
+ JNU_CHECK_EXCEPTION(env);
+
+ // Get cputime if for current process
+ if (pid == getpid()) {
+ struct rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage) != 0) {
+ return;
+ }
+ jlong microsecs =
+ usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec +
+ usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec;
+ totalTime = microsecs * 1000;
+ (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime);
+ JNU_CHECK_EXCEPTION(env);
+ }
+}
+
+/**
+ * Construct the argument array by parsing the arguments from the sequence of arguments.
+ */
+static int fillArgArray(JNIEnv *env, jobject jinfo, int nargs,
+ const char *cp, const char *argsEnd) {
+ jstring str = NULL;
+ jobject argsArray;
+ int i;
+
+ if (nargs < 1) {
+ return 0;
+ }
+ // Create a String array for nargs-1 elements
+ CHECK_NULL_RETURN((argsArray = (*env)->NewObjectArray(env,
+ nargs - 1, JNU_ClassString(env), NULL)), -1);
+
+ for (i = 0; i < nargs - 1; i++) {
+ // skip to the next argument; omits arg[0]
+ cp += strnlen(cp, (argsEnd - cp)) + 1;
+
+ if (cp > argsEnd || *cp == '\0') {
+ return -2; // Off the end pointer or an empty argument is an error
+ }
+
+ CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1);
+
+ (*env)->SetObjectArrayElement(env, argsArray, i, str);
+ JNU_CHECK_EXCEPTION_RETURN(env, -3);
+ }
+ (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray);
+ JNU_CHECK_EXCEPTION_RETURN(env, -4);
+ return 0;
+}
+
+/**
+ * Retrieve the command and arguments for the process and store them
+ * into the Info object.
+ */
+static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
+ int mib[3], maxargs, nargs, i;
+ size_t size;
+ char *args, *cp, *sp, *np;
+
+ // Get the maximum size of the arguments
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_ARGMAX;
+ size = sizeof(maxargs);
+ if (sysctl(mib, 2, &maxargs, &size, NULL, 0) == -1) {
+ JNU_ThrowByNameWithLastError(env,
+ "java/lang/RuntimeException", "sysctl failed");
+ return;
+ }
+
+ // Allocate an args buffer and get the arguments
+ args = (char *)malloc(maxargs);
+ if (args == NULL) {
+ JNU_ThrowOutOfMemoryError(env, "malloc failed");
+ return;
+ }
+
+ do { // a block to break out of on error
+ char *argsEnd;
+ jstring str = NULL;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROCARGS2;
+ mib[2] = pid;
+ size = (size_t) maxargs;
+ if (sysctl(mib, 3, args, &size, NULL, 0) == -1) {
+ if (errno != EINVAL) {
+ JNU_ThrowByNameWithLastError(env,
+ "java/lang/RuntimeException", "sysctl failed");
+ }
+ break;
+ }
+ memcpy(&nargs, args, sizeof(nargs));
+
+ cp = &args[sizeof(nargs)]; // Strings start after nargs
+ argsEnd = &args[size];
+
+ // Store the command executable path
+ if ((str = JNU_NewStringPlatform(env, cp)) == NULL) {
+ break;
+ }
+ (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, str);
+ if ((*env)->ExceptionCheck(env)) {
+ break;
+ }
+
+ // Skip trailing nulls after the executable path
+ for (cp = cp + strnlen(cp, argsEnd - cp); cp < argsEnd; cp++) {
+ if (*cp != '\0') {
+ break;
+ }
+ }
+
+ fillArgArray(env, jinfo, nargs, cp, argsEnd);
+ } while (0);
+ // Free the arg buffer
+ free(args);
+}
+
--- a/jdk/src/java.base/share/classes/java/io/InputStream.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/io/InputStream.java Thu Jun 04 18:49:37 2015 -0700
@@ -25,6 +25,7 @@
package java.io;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -50,7 +51,7 @@
// use when skipping.
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
- private static final int TRANSFER_BUFFER_SIZE = 8192;
+ private static final int DEFAULT_BUFFER_SIZE = 8192;
/**
* Reads the next byte of data from the input stream. The value byte is
@@ -192,6 +193,128 @@
}
/**
+ * The maximum size of array to allocate.
+ * Some VMs reserve some header words in an array.
+ * Attempts to allocate larger arrays may result in
+ * OutOfMemoryError: Requested array size exceeds VM limit
+ */
+ private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
+
+ /**
+ * Reads all remaining bytes from the input stream. This method blocks until
+ * all remaining bytes have been read and end of stream is detected, or an
+ * exception is thrown. This method does not close the input stream.
+ *
+ * <p> When this stream reaches end of stream, further invocations of this
+ * method will return an empty byte array.
+ *
+ * <p> Note that this method is intended for simple cases where it is
+ * convenient to read all bytes into a byte array. It is not intended for
+ * reading input streams with large amounts of data.
+ *
+ * <p> The behavior for the case where the input stream is <i>asynchronously
+ * closed</i>, or the thread interrupted during the read, is highly input
+ * stream specific, and therefore not specified.
+ *
+ * <p> If an I/O error occurs reading from the input stream, then it may do
+ * so after some, but not all, bytes have been read. Consequently the input
+ * stream may not be at end of stream and may be in an inconsistent state.
+ * It is strongly recommended that the stream be promptly closed if an I/O
+ * error occurs.
+ *
+ * @return a byte array containing the bytes read from this input stream
+ * @throws IOException if an I/O error occurs
+ * @throws OutOfMemoryError if an array of the required size cannot be
+ * allocated. For example, if an array larger than {@code 2GB} would
+ * be required to store the bytes.
+ *
+ * @since 1.9
+ */
+ public byte[] readAllBytes() throws IOException {
+ byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
+ int capacity = buf.length;
+ int nread = 0;
+ int n;
+ for (;;) {
+ // read to EOF which may read more or less than initial buffer size
+ while ((n = read(buf, nread, capacity - nread)) > 0)
+ nread += n;
+
+ // if the last call to read returned -1, then we're done
+ if (n < 0)
+ break;
+
+ // need to allocate a larger buffer
+ if (capacity <= MAX_BUFFER_SIZE - capacity) {
+ capacity = capacity << 1;
+ } else {
+ if (capacity == MAX_BUFFER_SIZE)
+ throw new OutOfMemoryError("Required array size too large");
+ capacity = MAX_BUFFER_SIZE;
+ }
+ buf = Arrays.copyOf(buf, capacity);
+ }
+ return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
+ }
+
+ /**
+ * Reads the requested number of bytes from the input stream into the given
+ * byte array. This method blocks until {@code len} bytes of input data have
+ * been read, end of stream is detected, or an exception is thrown. The
+ * number of bytes actually read, possibly zero, is returned. This method
+ * does not close the input stream.
+ *
+ * <p> In the case where end of stream is reached before {@code len} bytes
+ * have been read, then the actual number of bytes read will be returned.
+ * When this stream reaches end of stream, further invocations of this
+ * method will return zero.
+ *
+ * <p> If {@code len} is zero, then no bytes are read and {@code 0} is
+ * returned; otherwise, there is an attempt to read up to {@code len} bytes.
+ *
+ * <p> The first byte read is stored into element {@code b[off]}, the next
+ * one in to {@code b[off+1]}, and so on. The number of bytes read is, at
+ * most, equal to {@code len}. Let <i>k</i> be the number of bytes actually
+ * read; these bytes will be stored in elements {@code b[off]} through
+ * {@code b[off+}<i>k</i>{@code -1]}, leaving elements {@code b[off+}<i>k</i>
+ * {@code ]} through {@code b[off+len-1]} unaffected.
+ *
+ * <p> The behavior for the case where the input stream is <i>asynchronously
+ * closed</i>, or the thread interrupted during the read, is highly input
+ * stream specific, and therefore not specified.
+ *
+ * <p> If an I/O error occurs reading from the input stream, then it may do
+ * so after some, but not all, bytes of {@code b} have been updated with
+ * data from the input stream. Consequently the input stream and {@code b}
+ * may be in an inconsistent state. It is strongly recommended that the
+ * stream be promptly closed if an I/O error occurs.
+ *
+ * @param b the byte array into which the data is read
+ * @param off the start offset in {@code b} at which the data is written
+ * @param len the maximum number of bytes to read
+ * @return the actual number of bytes read into the buffer
+ * @throws IOException if an I/O error occurs
+ * @throws NullPointerException if {@code b} is {@code null}
+ * @throws IndexOutOfBoundsException If {@code off} is negative, {@code len}
+ * is negative, or {@code len} is greater than {@code b.length - off}
+ *
+ * @since 1.9
+ */
+ public int readNBytes(byte[] b, int off, int len) throws IOException {
+ Objects.requireNonNull(b);
+ if (off < 0 || len < 0 || len > b.length - off)
+ throw new IndexOutOfBoundsException();
+ int n = 0;
+ while (n < len) {
+ int count = read(b, off + n, len - n);
+ if (count < 0)
+ break;
+ n += count;
+ }
+ return n;
+ }
+
+ /**
* Skips over and discards <code>n</code> bytes of data from this input
* stream. The <code>skip</code> method may, for a variety of reasons, end
* up skipping over some smaller number of bytes, possibly <code>0</code>.
@@ -396,9 +519,9 @@
public long transferTo(OutputStream out) throws IOException {
Objects.requireNonNull(out, "out");
long transferred = 0;
- byte[] buffer = new byte[TRANSFER_BUFFER_SIZE];
+ byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int read;
- while ((read = this.read(buffer, 0, TRANSFER_BUFFER_SIZE)) >= 0) {
+ while ((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {
out.write(buffer, 0, read);
transferred += read;
}
--- a/jdk/src/java.base/share/classes/java/io/ObjectInputStream.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/io/ObjectInputStream.java Thu Jun 04 18:49:37 2015 -0700
@@ -253,9 +253,6 @@
/** flag set when at end of field value block with no TC_ENDBLOCKDATA */
private boolean defaultDataEnd = false;
- /** buffer for reading primitive field values */
- private byte[] primVals;
-
/** if true, invoke readObjectOverride() instead of readObject() */
private final boolean enableOverride;
/** if true, invoke resolveObject() */
@@ -500,7 +497,11 @@
Object curObj = ctx.getObj();
ObjectStreamClass curDesc = ctx.getDesc();
bin.setBlockDataMode(false);
- defaultReadFields(curObj, curDesc);
+ FieldValues vals = defaultReadFields(curObj, curDesc);
+ if (curObj != null) {
+ defaultCheckFieldValues(curObj, curDesc, vals);
+ defaultSetFieldValues(curObj, curDesc, vals);
+ }
bin.setBlockDataMode(true);
if (!curDesc.hasWriteObjectData()) {
/*
@@ -1881,6 +1882,26 @@
throws IOException
{
ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
+ // Best effort Failure Atomicity; slotValues will be non-null if field
+ // values can be set after reading all field data in the hierarchy.
+ // Field values can only be set after reading all data if there are no
+ // user observable methods in the hierarchy, readObject(NoData). The
+ // top most Serializable class in the hierarchy can be skipped.
+ FieldValues[] slotValues = null;
+
+ boolean hasSpecialReadMethod = false;
+ for (int i = 1; i < slots.length; i++) {
+ ObjectStreamClass slotDesc = slots[i].desc;
+ if (slotDesc.hasReadObjectMethod()
+ || slotDesc.hasReadObjectNoDataMethod()) {
+ hasSpecialReadMethod = true;
+ break;
+ }
+ }
+ // No special read methods, can store values and defer setting.
+ if (!hasSpecialReadMethod)
+ slotValues = new FieldValues[slots.length];
+
for (int i = 0; i < slots.length; i++) {
ObjectStreamClass slotDesc = slots[i].desc;
@@ -1917,7 +1938,13 @@
*/
defaultDataEnd = false;
} else {
- defaultReadFields(obj, slotDesc);
+ FieldValues vals = defaultReadFields(obj, slotDesc);
+ if (slotValues != null) {
+ slotValues[i] = vals;
+ } else if (obj != null) {
+ defaultCheckFieldValues(obj, slotDesc, vals);
+ defaultSetFieldValues(obj, slotDesc, vals);
+ }
}
if (slotDesc.hasWriteObjectData()) {
skipCustomData();
@@ -1933,6 +1960,19 @@
}
}
}
+
+ if (obj != null && slotValues != null) {
+ // Check that the non-primitive types are assignable for all slots
+ // before assigning.
+ for (int i = 0; i < slots.length; i++) {
+ if (slotValues[i] != null)
+ defaultCheckFieldValues(obj, slots[i].desc, slotValues[i]);
+ }
+ for (int i = 0; i < slots.length; i++) {
+ if (slotValues[i] != null)
+ defaultSetFieldValues(obj, slots[i].desc, slotValues[i]);
+ }
+ }
}
/**
@@ -1964,12 +2004,22 @@
}
}
+ private class FieldValues {
+ final byte[] primValues;
+ final Object[] objValues;
+
+ FieldValues(byte[] primValues, Object[] objValues) {
+ this.primValues = primValues;
+ this.objValues = objValues;
+ }
+ }
+
/**
* Reads in values of serializable fields declared by given class
- * descriptor. If obj is non-null, sets field values in obj. Expects that
- * passHandle is set to obj's handle before this method is called.
+ * descriptor. Expects that passHandle is set to obj's handle before this
+ * method is called.
*/
- private void defaultReadFields(Object obj, ObjectStreamClass desc)
+ private FieldValues defaultReadFields(Object obj, ObjectStreamClass desc)
throws IOException
{
Class<?> cl = desc.forClass();
@@ -1977,22 +2027,19 @@
throw new ClassCastException();
}
+ byte[] primVals = null;
int primDataSize = desc.getPrimDataSize();
if (primDataSize > 0) {
- if (primVals == null || primVals.length < primDataSize) {
- primVals = new byte[primDataSize];
- }
+ primVals = new byte[primDataSize];
bin.readFully(primVals, 0, primDataSize, false);
- if (obj != null) {
- desc.setPrimFieldValues(obj, primVals);
- }
}
+ Object[] objVals = null;
int numObjFields = desc.getNumObjFields();
if (numObjFields > 0) {
int objHandle = passHandle;
ObjectStreamField[] fields = desc.getFields(false);
- Object[] objVals = new Object[numObjFields];
+ objVals = new Object[numObjFields];
int numPrimFields = fields.length - objVals.length;
for (int i = 0; i < objVals.length; i++) {
ObjectStreamField f = fields[numPrimFields + i];
@@ -2001,11 +2048,30 @@
handles.markDependency(objHandle, passHandle);
}
}
- if (obj != null) {
- desc.setObjFieldValues(obj, objVals);
- }
passHandle = objHandle;
}
+
+ return new FieldValues(primVals, objVals);
+ }
+
+ /** Throws ClassCastException if any value is not assignable. */
+ private void defaultCheckFieldValues(Object obj, ObjectStreamClass desc,
+ FieldValues values) {
+ Object[] objectValues = values.objValues;
+ if (objectValues != null)
+ desc.checkObjFieldValueTypes(obj, objectValues);
+ }
+
+ /** Sets field values in obj. */
+ private void defaultSetFieldValues(Object obj, ObjectStreamClass desc,
+ FieldValues values) {
+ byte[] primValues = values.primValues;
+ Object[] objectValues = values.objValues;
+
+ if (primValues != null)
+ desc.setPrimFieldValues(obj, primValues);
+ if (objectValues != null)
+ desc.setObjFieldValues(obj, objectValues);
}
/**
--- a/jdk/src/java.base/share/classes/java/io/ObjectStreamClass.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/io/ObjectStreamClass.java Thu Jun 04 18:49:37 2015 -0700
@@ -1253,6 +1253,15 @@
}
/**
+ * Checks that the given values, from array vals starting at offset 0,
+ * are assignable to the given serializable object fields.
+ * @throws ClassCastException if any value is not assignable
+ */
+ void checkObjFieldValueTypes(Object obj, Object[] vals) {
+ fieldRefl.checkObjectFieldValueTypes(obj, vals);
+ }
+
+ /**
* Sets the serializable object fields of object obj using values from
* array vals starting at offset 0. It is the responsibility of the caller
* to ensure that obj is of the proper type if non-null.
@@ -2070,6 +2079,15 @@
}
/**
+ * Checks that the given values, from array vals starting at offset 0,
+ * are assignable to the given serializable object fields.
+ * @throws ClassCastException if any value is not assignable
+ */
+ void checkObjectFieldValueTypes(Object obj, Object[] vals) {
+ setObjFieldValues(obj, vals, true);
+ }
+
+ /**
* Sets the serializable object fields of object obj using values from
* array vals starting at offset 0. The caller is responsible for
* ensuring that obj is of the proper type; however, attempts to set a
@@ -2077,6 +2095,10 @@
* ClassCastException.
*/
void setObjFieldValues(Object obj, Object[] vals) {
+ setObjFieldValues(obj, vals, false);
+ }
+
+ private void setObjFieldValues(Object obj, Object[] vals, boolean dryRun) {
if (obj == null) {
throw new NullPointerException();
}
@@ -2101,7 +2123,8 @@
f.getType().getName() + " in instance of " +
obj.getClass().getName());
}
- unsafe.putObject(obj, key, val);
+ if (!dryRun)
+ unsafe.putObject(obj, key, val);
break;
default:
--- a/jdk/src/java.base/share/classes/java/lang/Character.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/Character.java Thu Jun 04 18:49:37 2015 -0700
@@ -646,13 +646,11 @@
*/
public static final class UnicodeBlock extends Subset {
/**
- * 510 - the expected number of enteties
+ * 510 - the expected number of entities
* 0.75 - the default load factor of HashMap
*/
- private static final int INITIAL_CAPACITY =
- (int)(510 / 0.75f + 1.0f);
private static Map<String, UnicodeBlock> map =
- new HashMap<>(INITIAL_CAPACITY);
+ new HashMap<>((int)(510 / 0.75f + 1.0f));
/**
* Creates a UnicodeBlock with the given identifier name.
--- a/jdk/src/java.base/share/classes/java/lang/Process.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/Process.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,25 +26,31 @@
package java.lang;
import java.io.*;
+import java.lang.ProcessBuilder.Redirect;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
/**
+ * {@code Process} provides control of native processes started by
+ * ProcessBuilder.start and Runtime.exec.
+ * The class provides methods for performing input from the process, performing
+ * output to the process, waiting for the process to complete,
+ * checking the exit status of the process, and destroying (killing)
+ * the process.
* The {@link ProcessBuilder#start()} and
* {@link Runtime#exec(String[],String[],File) Runtime.exec}
* methods create a native process and return an instance of a
* subclass of {@code Process} that can be used to control the process
- * and obtain information about it. The class {@code Process}
- * provides methods for performing input from the process, performing
- * output to the process, waiting for the process to complete,
- * checking the exit status of the process, and destroying (killing)
- * the process.
+ * and obtain information about it.
*
* <p>The methods that create processes may not work well for special
* processes on certain native platforms, such as native windowing
* processes, daemon processes, Win16/DOS processes on Microsoft
* Windows, or shell scripts.
*
- * <p>By default, the created subprocess does not have its own terminal
+ * <p>By default, the created process does not have its own terminal
* or console. All its standard I/O (i.e. stdin, stdout, stderr)
* operations will be redirected to the parent process, where they can
* be accessed via the streams obtained using the methods
@@ -52,35 +58,49 @@
* {@link #getInputStream()}, and
* {@link #getErrorStream()}.
* The parent process uses these streams to feed input to and get output
- * from the subprocess. Because some native platforms only provide
+ * from the process. Because some native platforms only provide
* limited buffer size for standard input and output streams, failure
* to promptly write the input stream or read the output stream of
- * the subprocess may cause the subprocess to block, or even deadlock.
+ * the process may cause the process to block, or even deadlock.
*
* <p>Where desired, <a href="ProcessBuilder.html#redirect-input">
- * subprocess I/O can also be redirected</a>
+ * process I/O can also be redirected</a>
* using methods of the {@link ProcessBuilder} class.
*
- * <p>The subprocess is not killed when there are no more references to
- * the {@code Process} object, but rather the subprocess
+ * <p>The process is not killed when there are no more references to
+ * the {@code Process} object, but rather the process
* continues executing asynchronously.
*
- * <p>There is no requirement that a process represented by a {@code
+ * <p>There is no requirement that the process represented by a {@code
* Process} object execute asynchronously or concurrently with respect
* to the Java process that owns the {@code Process} object.
*
* <p>As of 1.5, {@link ProcessBuilder#start()} is the preferred way
* to create a {@code Process}.
*
+ * <p>Subclasses of Process should override the {@link #onExit()} and
+ * {@link #toHandle()} methods to provide a fully functional Process including the
+ * {@link #getPid() process id},
+ * {@link #info() information about the process},
+ * {@link #children() direct children}, and
+ * {@link #allChildren() direct and indirect children} of the process.
+ * Delegating to the underlying Process or ProcessHandle is typically
+ * easiest and most efficient.
+ *
* @since 1.0
*/
public abstract class Process {
/**
+ * Default constructor for Process.
+ */
+ public Process() {}
+
+ /**
* Returns the output stream connected to the normal input of the
- * subprocess. Output to the stream is piped into the standard
+ * process. Output to the stream is piped into the standard
* input of the process represented by this {@code Process} object.
*
- * <p>If the standard input of the subprocess has been redirected using
+ * <p>If the standard input of the process has been redirected using
* {@link ProcessBuilder#redirectInput(Redirect)
* ProcessBuilder.redirectInput}
* then this method will return a
@@ -90,42 +110,42 @@
* output stream to be buffered.
*
* @return the output stream connected to the normal input of the
- * subprocess
+ * process
*/
public abstract OutputStream getOutputStream();
/**
* Returns the input stream connected to the normal output of the
- * subprocess. The stream obtains data piped from the standard
+ * process. The stream obtains data piped from the standard
* output of the process represented by this {@code Process} object.
*
- * <p>If the standard output of the subprocess has been redirected using
+ * <p>If the standard output of the process has been redirected using
* {@link ProcessBuilder#redirectOutput(Redirect)
* ProcessBuilder.redirectOutput}
* then this method will return a
* <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
*
- * <p>Otherwise, if the standard error of the subprocess has been
+ * <p>Otherwise, if the standard error of the process has been
* redirected using
* {@link ProcessBuilder#redirectErrorStream(boolean)
* ProcessBuilder.redirectErrorStream}
* then the input stream returned by this method will receive the
- * merged standard output and the standard error of the subprocess.
+ * merged standard output and the standard error of the process.
*
* <p>Implementation note: It is a good idea for the returned
* input stream to be buffered.
*
* @return the input stream connected to the normal output of the
- * subprocess
+ * process
*/
public abstract InputStream getInputStream();
/**
* Returns the input stream connected to the error output of the
- * subprocess. The stream obtains data piped from the error output
+ * process. The stream obtains data piped from the error output
* of the process represented by this {@code Process} object.
*
- * <p>If the standard error of the subprocess has been redirected using
+ * <p>If the standard error of the process has been redirected using
* {@link ProcessBuilder#redirectError(Redirect)
* ProcessBuilder.redirectError} or
* {@link ProcessBuilder#redirectErrorStream(boolean)
@@ -137,19 +157,19 @@
* input stream to be buffered.
*
* @return the input stream connected to the error output of
- * the subprocess
+ * the process
*/
public abstract InputStream getErrorStream();
/**
* Causes the current thread to wait, if necessary, until the
* process represented by this {@code Process} object has
- * terminated. This method returns immediately if the subprocess
- * has already terminated. If the subprocess has not yet
+ * terminated. This method returns immediately if the process
+ * has already terminated. If the process has not yet
* terminated, the calling thread will be blocked until the
- * subprocess exits.
+ * process exits.
*
- * @return the exit value of the subprocess represented by this
+ * @return the exit value of the process represented by this
* {@code Process} object. By convention, the value
* {@code 0} indicates normal termination.
* @throws InterruptedException if the current thread is
@@ -161,10 +181,10 @@
/**
* Causes the current thread to wait, if necessary, until the
- * subprocess represented by this {@code Process} object has
+ * process represented by this {@code Process} object has
* terminated, or the specified waiting time elapses.
*
- * <p>If the subprocess has already terminated then this method returns
+ * <p>If the process has already terminated then this method returns
* immediately with the value {@code true}. If the process has not
* terminated and the timeout value is less than, or equal to, zero, then
* this method returns immediately with the value {@code false}.
@@ -176,8 +196,8 @@
*
* @param timeout the maximum time to wait
* @param unit the time unit of the {@code timeout} argument
- * @return {@code true} if the subprocess has exited and {@code false} if
- * the waiting time elapsed before the subprocess has exited.
+ * @return {@code true} if the process has exited and {@code false} if
+ * the waiting time elapsed before the process has exited.
* @throws InterruptedException if the current thread is interrupted
* while waiting.
* @throws NullPointerException if unit is null
@@ -204,41 +224,60 @@
}
/**
- * Returns the exit value for the subprocess.
+ * Returns the exit value for the process.
*
- * @return the exit value of the subprocess represented by this
+ * @return the exit value of the process represented by this
* {@code Process} object. By convention, the value
* {@code 0} indicates normal termination.
- * @throws IllegalThreadStateException if the subprocess represented
+ * @throws IllegalThreadStateException if the process represented
* by this {@code Process} object has not yet terminated
*/
public abstract int exitValue();
/**
- * Kills the subprocess. Whether the subprocess represented by this
- * {@code Process} object is forcibly terminated or not is
+ * Kills the process.
+ * Whether the process represented by this {@code Process} object is
+ * {@link #supportsNormalTermination normally terminated} or not is
* implementation dependent.
+ * Forcible process destruction is defined as the immediate termination of a
+ * process, whereas normal termination allows the process to shut down cleanly.
+ * If the process is not alive, no action is taken.
+ * <p>
+ * The {@link java.util.concurrent.CompletableFuture} from {@link #onExit} is
+ * {@link java.util.concurrent.CompletableFuture#complete completed}
+ * when the process has terminated.
*/
public abstract void destroy();
/**
- * Kills the subprocess. The subprocess represented by this
+ * Kills the process forcibly. The process represented by this
* {@code Process} object is forcibly terminated.
+ * Forcible process destruction is defined as the immediate termination of a
+ * process, whereas normal termination allows the process to shut down cleanly.
+ * If the process is not alive, no action is taken.
+ * <p>
+ * The {@link java.util.concurrent.CompletableFuture} from {@link #onExit} is
+ * {@link java.util.concurrent.CompletableFuture#complete completed}
+ * when the process has terminated.
+ * <p>
+ * Invoking this method on {@code Process} objects returned by
+ * {@link ProcessBuilder#start} and {@link Runtime#exec} forcibly terminate
+ * the process.
*
- * <p>The default implementation of this method invokes {@link #destroy}
- * and so may not forcibly terminate the process. Concrete implementations
- * of this class are strongly encouraged to override this method with a
- * compliant implementation. Invoking this method on {@code Process}
- * objects returned by {@link ProcessBuilder#start} and
- * {@link Runtime#exec} will forcibly terminate the process.
- *
- * <p>Note: The subprocess may not terminate immediately.
+ * @implSpec
+ * The default implementation of this method invokes {@link #destroy}
+ * and so may not forcibly terminate the process.
+ * @implNote
+ * Concrete implementations of this class are strongly encouraged to override
+ * this method with a compliant implementation.
+ * @apiNote
+ * The process may not terminate immediately.
* i.e. {@code isAlive()} may return true for a brief period
* after {@code destroyForcibly()} is called. This method
* may be chained to {@code waitFor()} if needed.
*
* @return the {@code Process} object representing the
- * subprocess to be forcibly destroyed.
+ * process forcibly destroyed
* @since 1.8
*/
public Process destroyForcibly() {
@@ -247,10 +286,36 @@
}
/**
- * Tests whether the subprocess represented by this {@code Process} is
+ * Returns {@code true} if the implementation of {@link #destroy} is to
+ * normally terminate the process,
+ * Returns {@code false} if the implementation of {@code destroy}
+ * forcibly and immediately terminates the process.
+ * <p>
+ * Invoking this method on {@code Process} objects returned by
+ * {@link ProcessBuilder#start} and {@link Runtime#exec} return
+ * {@code true} or {@code false} depending on the platform implementation.
+ *
+ * @implSpec
+ * This implementation throws an instance of
+ * {@link java.lang.UnsupportedOperationException} and performs no other action.
+ *
+ * @return {@code true} if the implementation of {@link #destroy} is to
+ * normally terminate the process;
+ * otherwise, {@link #destroy} forcibly terminates the process
+ * @throws UnsupportedOperationException if the Process implementation
+ * does not support this operation
+ * @since 1.9
+ */
+ public boolean supportsNormalTermination() {
+ throw new UnsupportedOperationException(this.getClass()
+ + ".supportsNormalTermination() not supported" );
+ }
+
+ /**
+ * Tests whether the process represented by this {@code Process} is
* alive.
*
- * @return {@code true} if the subprocess represented by this
+ * @return {@code true} if the process represented by this
* {@code Process} object has not yet terminated.
* @since 1.8
*/
@@ -264,16 +329,222 @@
}
/**
- * Returns the native process id of the subprocess.
- * The native process id is an identification number that the operating
+ * Returns the native process ID of the process.
+ * The native process ID is an identification number that the operating
* system assigns to the process.
*
- * @return the native process id of the subprocess
+ * @implSpec
+ * The implementation of this method returns the process id as:
+ * {@link #toHandle toHandle().getPid()}.
+ *
+ * @return the native process id of the process
* @throws UnsupportedOperationException if the Process implementation
- * does not support this operation
+ * does not support this operation
* @since 1.9
*/
public long getPid() {
- throw new UnsupportedOperationException();
+ return toHandle().getPid();
+ }
+
+ /**
+ * Returns a {@code CompletableFuture<Process>} for the termination of the Process.
+ * The {@link java.util.concurrent.CompletableFuture} provides the ability
+ * to trigger dependent functions or actions that may be run synchronously
+ * or asynchronously upon process termination.
+ * When the process terminates the CompletableFuture is
+ * {@link java.util.concurrent.CompletableFuture#complete completed} regardless
+ * of the exit status of the process.
+ * <p>
+ * Calling {@code onExit().get()} waits for the process to terminate and returns
+ * the Process. The future can be used to check if the process is
+ * {@link java.util.concurrent.CompletableFuture#isDone done} or to
+ * {@link java.util.concurrent.CompletableFuture#get() wait} for it to terminate.
+ * {@link java.util.concurrent.CompletableFuture#cancel(boolean) Cancelling}
+ * the CompletableFuture does not affect the Process.
+ * <p>
+ * If the process is {@link #isAlive not alive} the {@link CompletableFuture}
+ * returned has been {@link java.util.concurrent.CompletableFuture#complete completed}.
+ * <p>
+ * Processes returned from {@link ProcessBuilder#start} override the
+ * default implementation to provide an efficient mechanism to wait
+ * for process exit.
+ * <p>
+ * @apiNote
+ * Using {@link #onExit() onExit} is an alternative to
+ * {@link #waitFor() waitFor} that enables both additional concurrency
+ * and convenient access to the result of the Process.
+ * Lambda expressions can be used to evaluate the result of the Process
+ * execution.
+ * If there is other processing to be done before the value is used
+ * then {@linkplain #onExit onExit} is a convenient mechanism to
+ * free the current thread and block only if and when the value is needed.
+ * <br>
+ * For example, launching a process to compare two files and get a boolean if they are identical:
+ * <pre> {@code Process p = new ProcessBuilder("cmp", "f1", "f2").start();
+ * Future<Boolean> identical = p.onExit().thenApply(p1 -> p1.exitValue() == 0);
+ * ...
+ * if (identical.get()) { ... }
+ * }</pre>
+ *
+ * @implSpec
+ * This implementation executes {@link #waitFor()} in a separate thread
+ * repeatedly until it returns successfully. If the execution of
+ * {@code waitFor} is interrupted, the thread's interrupt status is preserved.
+ * <p>
+ * When {@link #waitFor()} returns successfully the CompletableFuture is
+ * {@link java.util.concurrent.CompletableFuture#complete completed} regardless
+ * of the exit status of the process.
+ *
+ * This implementation may consume a lot of memory for thread stacks if a
+ * large number of processes are waited for concurrently.
+ * <p>
+ * External implementations should override this method and provide
+ * a more efficient implementation. For example, to delegate to the underlying
+ * process, it can do the following:
+ * <pre>{@code
+ * public CompletableFuture<Process> onExit() {
+ * return delegate.onExit().thenApply(p -> this);
+ * }
+ * }</pre>
+ *
+ * @return a new {@code CompletableFuture<Process>} for the Process
+ *
+ * @since 1.9
+ */
+ public CompletableFuture<Process> onExit() {
+ return CompletableFuture.supplyAsync(this::waitForInternal);
}
+
+ /**
+ * Wait for the process to exit by calling {@code waitFor}.
+ * If the thread is interrupted, remember the interrupted state to
+ * be restored before returning. Use ForkJoinPool.ManagedBlocker
+ * so that the number of workers in case ForkJoinPool is used is
+ * compensated when the thread blocks in waitFor().
+ *
+ * @return the Process
+ */
+ private Process waitForInternal() {
+ boolean interrupted = false;
+ while (true) {
+ try {
+ ForkJoinPool.managedBlock(new ForkJoinPool.ManagedBlocker() {
+ @Override
+ public boolean block() throws InterruptedException {
+ waitFor();
+ return true;
+ }
+
+ @Override
+ public boolean isReleasable() {
+ return !isAlive();
+ }
+ });
+ break;
+ } catch (InterruptedException x) {
+ interrupted = true;
+ }
+ }
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ return this;
+ }
+
+ /**
+ * Returns a ProcessHandle for the Process.
+ *
+ * {@code Process} objects returned by {@link ProcessBuilder#start} and
+ * {@link Runtime#exec} implement {@code toHandle} as the equivalent of
+ * {@link ProcessHandle#of(long) ProcessHandle.of(pid)} including the
+ * check for a SecurityManager and {@code RuntimePermission("manageProcess")}.
+ *
+ * @implSpec
+ * This implementation throws an instance of
+ * {@link java.lang.UnsupportedOperationException} and performs no other action.
+ * Subclasses should override this method to provide a ProcessHandle for the
+ * process. The methods {@link #getPid}, {@link #info}, {@link #children},
+ * and {@link #allChildren}, unless overridden, operate on the ProcessHandle.
+ *
+ * @return Returns a ProcessHandle for the Process
+ * @throws UnsupportedOperationException if the Process implementation
+ * does not support this operation
+ * @throws SecurityException if a security manager has been installed and
+ * it denies RuntimePermission("manageProcess")
+ * @since 1.9
+ */
+ public ProcessHandle toHandle() {
+ throw new UnsupportedOperationException(this.getClass()
+ + ".toHandle() not supported");
+ }
+
+ /**
+ * Returns a snapshot of information about the process.
+ *
+ * <p> An {@link ProcessHandle.Info} instance has various accessor methods
+ * that return information about the process, if the process is alive and
+ * the information is available, otherwise {@code null} is returned.
+ *
+ * @implSpec
+ * This implementation returns information about the process as:
+ * {@link #toHandle toHandle().info()}.
+ *
+ * @return a snapshot of information about the process, always non-null
+ * @throws UnsupportedOperationException if the Process implementation
+ * does not support this operation
+ * @since 1.9
+ */
+ public ProcessHandle.Info info() {
+ return toHandle().info();
+ }
+
+ /**
+ * Returns a snapshot of the direct children of the process.
+ * A process that is {@link #isAlive not alive} has zero children.
+ * <p>
+ * <em>Note that processes are created and terminate asynchronously.
+ * There is no guarantee that a process is {@link #isAlive alive}.
+ * </em>
+ *
+ * @implSpec
+ * This implementation returns the direct children as:
+ * {@link #toHandle toHandle().children()}.
+ *
+ * @return a Stream of ProcessHandles for processes that are direct children
+ * of the process
+ * @throws UnsupportedOperationException if the Process implementation
+ * does not support this operation
+ * @throws SecurityException if a security manager has been installed and
+ * it denies RuntimePermission("manageProcess")
+ * @since 1.9
+ */
+ public Stream<ProcessHandle> children() {
+ return toHandle().children();
+ }
+
+ /**
+ * Returns a snapshot of the direct and indirect children of the process.
+ * A process that is {@link #isAlive not alive} has zero children.
+ * <p>
+ * <em>Note that processes are created and terminate asynchronously.
+ * There is no guarantee that a process is {@link #isAlive alive}.
+ * </em>
+ *
+ * @implSpec
+ * This implementation returns all children as:
+ * {@link #toHandle toHandle().allChildren()}.
+ *
+ * @return a Stream of ProcessHandles for processes that are direct and
+ * indirect children of the process
+ * @throws UnsupportedOperationException if the Process implementation
+ * does not support this operation
+ * @throws SecurityException if a security manager has been installed and
+ * it denies RuntimePermission("manageProcess")
+ * @since 1.9
+ */
+ public Stream<ProcessHandle> allChildren() {
+ return toHandle().allChildren();
+ }
+
+
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/ProcessHandle.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.lang;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Stream;
+
+/**
+ * ProcessHandle identifies and provides control of native processes. Each
+ * individual process can be monitored for liveness, list its children,
+ * get information about the process or destroy it.
+ * By comparison, {@link java.lang.Process Process} instances were started
+ * by the current process and additionally provide access to the process
+ * input, output, and error streams.
+ * <p>
+ * The native process ID is an identification number that the
+ * operating system assigns to the process.
+ * The range for process id values is dependent on the operating system.
+ * For example, an embedded system might use a 16-bit value.
+ * Status information about a process is retrieved from the native system
+ * and may change asynchronously; processes may be created or terminate
+ * spontaneously.
+ * The time between when a process terminates and the process id
+ * is reused for a new process is unpredictable.
+ * Race conditions can exist between checking the status of a process and
+ * acting upon it. When using ProcessHandles avoid assumptions
+ * about the liveness or identity of the underlying process.
+ * <p>
+ * Each ProcessHandle identifies and allows control of a process in the native
+ * system. ProcessHandles are returned from the factory methods {@link #current()},
+ * {@link #of(long)},
+ * {@link #children}, {@link #allChildren}, {@link #parent()} and
+ * {@link #allProcesses()}.
+ * <p>
+ * The {@link Process} instances created by {@link ProcessBuilder} can be queried
+ * for a ProcessHandle that provides information about the Process.
+ * ProcessHandle references should not be freely distributed.
+ *
+ * <p>
+ * A {@link java.util.concurrent.CompletableFuture} available from {@link #onExit}
+ * can be used to wait for process termination, and possibly trigger dependent
+ * actions.
+ * <p>
+ * The factory methods limit access to ProcessHandles using the
+ * SecurityManager checking the {@link RuntimePermission RuntimePermission("manageProcess")}.
+ * The ability to control processes is also restricted by the native system,
+ * ProcessHandle provides no more access to, or control over, the native process
+ * than would be allowed by a native application.
+ * <p>
+ * @implSpec
+ * In the case where ProcessHandles cannot be supported then the factory
+ * methods must consistently throw {@link java.lang.UnsupportedOperationException}.
+ * The methods of this class throw {@link java.lang.UnsupportedOperationException}
+ * if the operating system does not allow access to query or kill a process.
+ *
+ * @see Process
+ * @since 1.9
+ */
+public interface ProcessHandle extends Comparable<ProcessHandle> {
+
+ /**
+ * Returns the native process ID of the process. The native process ID is an
+ * identification number that the operating system assigns to the process.
+ *
+ * @return the native process ID of the process
+ * @throws UnsupportedOperationException if the implementation
+ * does not support this operation
+ */
+ long getPid();
+
+ /**
+ * Returns an {@code Optional<ProcessHandle>} for an existing native process.
+ *
+ * @param pid a native process ID
+ * @return an {@code Optional<ProcessHandle>} of the PID for the process;
+ * the {@code Optional} is empty if the process does not exist
+ * @throws SecurityException if a security manager has been installed and
+ * it denies RuntimePermission("manageProcess")
+ * @throws UnsupportedOperationException if the implementation
+ * does not support this operation
+ */
+ public static Optional<ProcessHandle> of(long pid) {
+ return ProcessHandleImpl.get(pid);
+ }
+
+ /**
+ * Returns a ProcessHandle for the current process. The ProcessHandle cannot be
+ * used to destroy the current process, use {@link System#exit System.exit} instead.
+ *
+ * @return a ProcessHandle for the current process
+ * @throws SecurityException if a security manager has been installed and
+ * it denies RuntimePermission("manageProcess")
+ * @throws UnsupportedOperationException if the implementation
+ * does not support this operation
+ */
+ public static ProcessHandle current() {
+ return ProcessHandleImpl.current();
+ }
+
+ /**
+ * Returns an {@code Optional<ProcessHandle>} for the parent process.
+ * Note that Processes in a zombie state usually don't have a parent.
+ *
+ * @return an {@code Optional<ProcessHandle>} of the parent process;
+ * the {@code Optional} is empty if the child process does not have a parent
+ * or if the parent is not available, possibly due to operating system limitations
+ * @throws SecurityException if a security manager has been installed and
+ * it denies RuntimePermission("manageProcess")
+ */
+ Optional<ProcessHandle> parent();
+
+ /**
+ * Returns a snapshot of the current direct children of the process.
+ * A process that is {@link #isAlive not alive} has zero children.
+ * <p>
+ * <em>Note that processes are created and terminate asynchronously.
+ * There is no guarantee that a process is {@link #isAlive alive}.
+ * </em>
+ *
+ * @return a Stream of ProcessHandles for processes that are direct children
+ * of the process
+ * @throws SecurityException if a security manager has been installed and
+ * it denies RuntimePermission("manageProcess")
+ */
+ Stream<ProcessHandle> children();
+
+ /**
+ * Returns a snapshot of the current direct and indirect children of the process.
+ * A process that is {@link #isAlive not alive} has zero children.
+ * <p>
+ * <em>Note that processes are created and terminate asynchronously.
+ * There is no guarantee that a process is {@link #isAlive alive}.
+ * </em>
+ *
+ * @return a Stream of ProcessHandles for processes that are direct and
+ * indirect children of the process
+ * @throws SecurityException if a security manager has been installed and
+ * it denies RuntimePermission("manageProcess")
+ */
+ Stream<ProcessHandle> allChildren();
+
+ /**
+ * Returns a snapshot of all processes visible to the current process.
+ * <p>
+ * <em>Note that processes are created and terminate asynchronously. There
+ * is no guarantee that a process in the stream is alive or that no other
+ * processes may have been created since the inception of the snapshot.
+ * </em>
+ *
+ * @return a Stream of ProcessHandles for all processes
+ * @throws SecurityException if a security manager has been installed and
+ * it denies RuntimePermission("manageProcess")
+ * @throws UnsupportedOperationException if the implementation
+ * does not support this operation
+ */
+ static Stream<ProcessHandle> allProcesses() {
+ return ProcessHandleImpl.children(0);
+ }
+
+ /**
+ * Returns a snapshot of information about the process.
+ *
+ * <p> An {@code Info} instance has various accessor methods that return
+ * information about the process, if the process is alive and the
+ * information is available.
+ *
+ * @return a snapshot of information about the process, always non-null
+ */
+ Info info();
+
+ /**
+ * Information snapshot about the process.
+ * The attributes of a process vary by operating system and are not available
+ * in all implementations. Information about processes is limited
+ * by the operating system privileges of the process making the request.
+ * The return types are {@code Optional<T>} allowing explicit tests
+ * and actions if the value is available.
+ * @since 1.9
+ */
+ public interface Info {
+ /**
+ * Returns the executable pathname of the process.
+ *
+ * @return an {@code Optional<String>} of the executable pathname
+ * of the process
+ */
+ public Optional<String> command();
+
+ /**
+ * Returns an array of Strings of the arguments of the process.
+ *
+ * @return an {@code Optional<String[]>} of the arguments of the process
+ */
+ public Optional<String[]> arguments();
+
+ /**
+ * Returns the start time of the process.
+ *
+ * @return an {@code Optional<Instant>} of the start time of the process
+ */
+ public Optional<Instant> startInstant();
+
+ /**
+ * Returns the total cputime accumulated of the process.
+ *
+ * @return an {@code Optional<Duration>} for the accumulated total cputime
+ */
+ public Optional<Duration> totalCpuDuration();
+
+ /**
+ * Return the user of the process.
+ *
+ * @return an {@code Optional<String>} for the user of the process
+ */
+ public Optional<String> user();
+ }
+
+ /**
+ * Returns a {@code CompletableFuture<ProcessHandle>} for the termination
+ * of the process.
+ * The {@link java.util.concurrent.CompletableFuture} provides the ability
+ * to trigger dependent functions or actions that may be run synchronously
+ * or asynchronously upon process termination.
+ * When the process terminates the CompletableFuture is
+ * {@link java.util.concurrent.CompletableFuture#complete completed} regardless
+ * of the exit status of the process.
+ * The {@code onExit} method can be called multiple times to invoke
+ * independent actions when the process exits.
+ * <p>
+ * Calling {@code onExit().get()} waits for the process to terminate and returns
+ * the ProcessHandle. The future can be used to check if the process is
+ * {@link java.util.concurrent.CompletableFuture#isDone done} or to
+ * {@link java.util.concurrent.Future#get() wait} for it to terminate.
+ * {@link java.util.concurrent.Future#cancel(boolean) Cancelling}
+ * the CompleteableFuture does not affect the Process.
+ * <p>
+ * If the process is {@link #isAlive not alive} the {@link CompletableFuture}
+ * returned has been {@link java.util.concurrent.CompletableFuture#complete completed}.
+ *
+ * @return a new {@code CompletableFuture<ProcessHandle>} for the ProcessHandle
+ *
+ * @throws IllegalStateException if the process is the current process
+ */
+ CompletableFuture<ProcessHandle> onExit();
+
+ /**
+ * Returns {@code true} if the implementation of {@link #destroy}
+ * normally terminates the process.
+ * Returns {@code false} if the implementation of {@code destroy}
+ * forcibly and immediately terminates the process.
+ *
+ * @return {@code true} if the implementation of {@link #destroy}
+ * normally terminates the process;
+ * otherwise, {@link #destroy} forcibly terminates the process
+ */
+ boolean supportsNormalTermination();
+
+ /**
+ * Requests the process to be killed.
+ * Whether the process represented by this {@code ProcessHandle} object is
+ * {@link #supportsNormalTermination normally terminated} or not is
+ * implementation dependent.
+ * Forcible process destruction is defined as the immediate termination of the
+ * process, whereas normal termination allows the process to shut down cleanly.
+ * If the process is not alive, no action is taken.
+ * The operating system access controls may prevent the process
+ * from being killed.
+ * <p>
+ * The {@link java.util.concurrent.CompletableFuture} from {@link #onExit} is
+ * {@link java.util.concurrent.CompletableFuture#complete completed}
+ * when the process has terminated.
+ * <p>
+ * Note: The process may not terminate immediately.
+ * For example, {@code isAlive()} may return true for a brief period
+ * after {@code destroy()} is called.
+ *
+ * @return {@code true} if termination was successfully requested,
+ * otherwise {@code false}
+ * @throws IllegalStateException if the process is the current process
+ */
+ boolean destroy();
+
+ /**
+ * Requests the process to be killed forcibly.
+ * The process represented by this {@code ProcessHandle} object is
+ * forcibly terminated.
+ * Forcible process destruction is defined as the immediate termination of the
+ * process, whereas normal termination allows the process to shut down cleanly.
+ * If the process is not alive, no action is taken.
+ * The operating system access controls may prevent the process
+ * from being killed.
+ * <p>
+ * The {@link java.util.concurrent.CompletableFuture} from {@link #onExit} is
+ * {@link java.util.concurrent.CompletableFuture#complete completed}
+ * when the process has terminated.
+ * <p>
+ * Note: The process may not terminate immediately.
+ * For example, {@code isAlive()} may return true for a brief period
+ * after {@code destroyForcibly()} is called.
+ *
+ * @return {@code true} if termination was successfully requested,
+ * otherwise {@code false}
+ * @throws IllegalStateException if the process is the current process
+ */
+ boolean destroyForcibly();
+
+ /**
+ * Tests whether the process represented by this {@code ProcessHandle} is alive.
+ * Process termination is implementation and operating system specific.
+ * The process is considered alive as long as the PID is valid.
+ *
+ * @return {@code true} if the process represented by this
+ * {@code ProcessHandle} object has not yet terminated
+ */
+ boolean isAlive();
+
+ /**
+ * Compares this ProcessHandle with the specified ProcessHandle for order.
+ * The order is not specified, but is consistent with {@link Object#equals},
+ * which returns {@code true} if and only if two instances of ProcessHandle
+ * are of the same implementation and represent the same system process.
+ * Comparison is only supported among objects of same implementation.
+ * If attempt is made to mutually compare two different implementations
+ * of {@link ProcessHandle}s, {@link ClassCastException} is thrown.
+ *
+ * @param other the ProcessHandle to be compared
+ * @return a negative integer, zero, or a positive integer as this object
+ * is less than, equal to, or greater than the specified object.
+ * @throws NullPointerException if the specified object is null
+ * @throws ClassCastException if the specified object is not of same class
+ * as this object
+ */
+ @Override
+ int compareTo(ProcessHandle other);
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.lang;
+
+import java.security.PrivilegedAction;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import sun.misc.InnocuousThread;
+
+import static java.security.AccessController.doPrivileged;
+
+/**
+ * ProcessHandleImpl is the implementation of ProcessHandle.
+ *
+ * @see Process
+ * @since 1.9
+ */
+final class ProcessHandleImpl implements ProcessHandle {
+
+ /**
+ * The thread pool of "process reaper" daemon threads.
+ */
+ private static final Executor processReaperExecutor =
+ doPrivileged((PrivilegedAction<Executor>) () -> {
+
+ ThreadGroup tg = Thread.currentThread().getThreadGroup();
+ while (tg.getParent() != null) tg = tg.getParent();
+ ThreadGroup systemThreadGroup = tg;
+
+ ThreadFactory threadFactory = grimReaper -> {
+ // Our thread stack requirement is quite modest.
+ Thread t = new Thread(systemThreadGroup, grimReaper,
+ "process reaper", 32768);
+ t.setDaemon(true);
+ // A small attempt (probably futile) to avoid priority inversion
+ t.setPriority(Thread.MAX_PRIORITY);
+ return t;
+ };
+
+ return Executors.newCachedThreadPool(threadFactory);
+ });
+
+ private static class ExitCompletion extends CompletableFuture<Integer> {
+ final boolean isReaping;
+
+ ExitCompletion(boolean isReaping) {
+ this.isReaping = isReaping;
+ }
+ }
+
+ private static final ConcurrentMap<Long, ExitCompletion>
+ completions = new ConcurrentHashMap<>();
+
+ /**
+ * Returns a CompletableFuture that completes with process exit status when
+ * the process completes.
+ *
+ * @param shouldReap true if the exit value should be reaped
+ */
+ static CompletableFuture<Integer> completion(long pid, boolean shouldReap) {
+ // check canonicalizing cache 1st
+ ExitCompletion completion = completions.get(pid);
+ // re-try until we get a completion that shouldReap => isReaping
+ while (completion == null || (shouldReap && !completion.isReaping)) {
+ ExitCompletion newCompletion = new ExitCompletion(shouldReap);
+ if (completion == null) {
+ completion = completions.putIfAbsent(pid, newCompletion);
+ } else {
+ completion = completions.replace(pid, completion, newCompletion)
+ ? null : completions.get(pid);
+ }
+ if (completion == null) {
+ // newCompletion has just been installed successfully
+ completion = newCompletion;
+ // spawn a thread to wait for and deliver the exit value
+ processReaperExecutor.execute(() -> {
+ int exitValue = waitForProcessExit0(pid, shouldReap);
+ newCompletion.complete(exitValue);
+ // remove from cache afterwards
+ completions.remove(pid, newCompletion);
+ });
+ }
+ }
+ return completion;
+ }
+
+ @Override
+ public CompletableFuture<ProcessHandle> onExit() {
+ if (this.equals(current)) {
+ throw new IllegalStateException("onExit for current process not allowed");
+ }
+
+ return ProcessHandleImpl.completion(getPid(), false)
+ .handleAsync((exitStatus, unusedThrowable) -> this);
+ }
+
+ /**
+ * Wait for the process to exit, return the value.
+ * Conditionally reap the value if requested
+ * @param pid the processId
+ * @param reapvalue if true, the value is retrieved,
+ * else return the value and leave the process waitable
+ *
+ * @return the value or -1 if an error occurs
+ */
+ private static native int waitForProcessExit0(long pid, boolean reapvalue);
+
+ /**
+ * Cache the ProcessHandle of this process.
+ */
+ private static final ProcessHandleImpl current =
+ new ProcessHandleImpl(getCurrentPid0());
+
+ /**
+ * The pid of this ProcessHandle.
+ */
+ private final long pid;
+
+ /**
+ * Private constructor. Instances are created by the {@code get(long)} factory.
+ * @param pid the pid for this instance
+ */
+ private ProcessHandleImpl(long pid) {
+ this.pid = pid;
+ }
+
+ /**
+ * Returns a ProcessHandle for an existing native process.
+ *
+ * @param pid the native process identifier
+ * @return The ProcessHandle for the pid if the process is alive;
+ * or {@code null} if the process ID does not exist in the native system.
+ * @throws SecurityException if RuntimePermission("manageProcess") is not granted
+ */
+ static Optional<ProcessHandle> get(long pid) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("manageProcess"));
+ }
+ return Optional.ofNullable(isAlive0(pid) ? new ProcessHandleImpl(pid) : null);
+ }
+
+ /**
+ * Returns a ProcessHandle corresponding known to exist pid.
+ * Called from ProcessImpl, it does not perform a security check or check if the process is alive.
+ * @param pid of the known to exist process
+ * @return a ProcessHandle corresponding to an existing Process instance
+ */
+ static ProcessHandle getUnchecked(long pid) {
+ return new ProcessHandleImpl(pid);
+ }
+
+ /**
+ * Returns the native process ID.
+ * A {@code long} is used to be able to fit the system specific binary values
+ * for the process.
+ *
+ * @return the native process ID
+ */
+ @Override
+ public long getPid() {
+ return pid;
+ }
+
+ /**
+ * Returns the ProcessHandle for the current native process.
+ *
+ * @return The ProcessHandle for the OS process.
+ * @throws SecurityException if RuntimePermission("manageProcess") is not granted
+ */
+ public static ProcessHandleImpl current() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("manageProcess"));
+ }
+ return current;
+ }
+
+ /**
+ * Return the pid of the current process.
+ *
+ * @return the pid of the current process
+ */
+ private static native long getCurrentPid0();
+
+ /**
+ * Returns a ProcessHandle for the parent process.
+ *
+ * @return a ProcessHandle of the parent process; {@code null} is returned
+ * if the child process does not have a parent
+ * @throws SecurityException if permission is not granted by the
+ * security policy
+ */
+ static Optional<ProcessHandle> parent(long pid) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("manageProcess"));
+ }
+ long ppid = parent0(pid);
+ if (ppid <= 0) {
+ return Optional.empty();
+ }
+ return get(ppid);
+ }
+
+ /**
+ * Returns the parent of the native pid argument.
+ *
+ * @return the parent of the native pid; if any, otherwise -1
+ */
+ private static native long parent0(long pid);
+
+ /**
+ * Returns the number of pids filled in to the array.
+ * @param pid if {@code pid} equals zero, then all known processes are returned;
+ * otherwise only direct child process pids are returned
+ * @param pids an allocated long array to receive the pids
+ * @param ppids an allocated long array to receive the parent pids; may be null
+ * @return if greater than or equals to zero is the number of pids in the array;
+ * if greater than the length of the arrays, the arrays are too small
+ */
+ private static native int getProcessPids0(long pid, long[] pids, long[] ppids);
+
+ /**
+ * Destroy the process for this ProcessHandle.
+ * @param pid the processs ID to destroy
+ * @param force {@code true} if the process should be terminated forcibly;
+ * else {@code false} for a normal termination
+ */
+ static void destroyProcess(long pid, boolean force) {
+ destroy0(pid, force);
+ }
+
+ private static native boolean destroy0(long pid, boolean forcibly);
+
+ @Override
+ public boolean destroy() {
+ if (this.equals(current)) {
+ throw new IllegalStateException("destroy of current process not allowed");
+ }
+ return destroy0(getPid(), false);
+ }
+
+ @Override
+ public boolean destroyForcibly() {
+ if (this.equals(current)) {
+ throw new IllegalStateException("destroy of current process not allowed");
+ }
+ return destroy0(getPid(), true);
+ }
+
+
+ @Override
+ public boolean supportsNormalTermination() {
+ return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
+ }
+
+ /**
+ * Tests whether the process represented by this {@code ProcessHandle} is alive.
+ *
+ * @return {@code true} if the process represented by this
+ * {@code ProcessHandle} object has not yet terminated.
+ * @since 1.9
+ */
+ @Override
+ public boolean isAlive() {
+ return isAlive0(pid);
+ }
+
+ /**
+ * Returns true or false depending on whether the pid is alive.
+ * This must not reap the exitValue like the isAlive method above.
+ *
+ * @param pid the pid to check
+ * @return true or false
+ */
+ private static native boolean isAlive0(long pid);
+
+ @Override
+ public Optional<ProcessHandle> parent() {
+ return parent(pid);
+ }
+
+ @Override
+ public Stream<ProcessHandle> children() {
+ return children(pid);
+ }
+
+ /**
+ * Returns a Stream of the children of a process or all processes.
+ *
+ * @param pid the pid of the process for which to find the children;
+ * 0 for all processes
+ * @return a stream of ProcessHandles
+ */
+ static Stream<ProcessHandle> children(long pid) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("manageProcess"));
+ }
+ int size = 100;
+ long[] childpids = null;
+ while (childpids == null || size > childpids.length) {
+ childpids = new long[size];
+ size = getProcessPids0(pid, childpids, null);
+ }
+ return Arrays.stream(childpids, 0, size).mapToObj((id) -> new ProcessHandleImpl(id));
+ }
+
+ @Override
+ public Stream<ProcessHandle> allChildren() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("manageProcess"));
+ }
+ int size = 100;
+ long[] pids = null;
+ long[] ppids = null;
+ while (pids == null || size > pids.length) {
+ pids = new long[size];
+ ppids = new long[size];
+ size = getProcessPids0(0, pids, ppids);
+ }
+
+ int next = 0; // index of next process to check
+ int count = -1; // count of subprocesses scanned
+ long ppid = pid; // start looking for this parent
+ do {
+ // Scan from next to size looking for ppid
+ // if found, exchange it to index next
+ for (int i = next; i < size; i++) {
+ if (ppids[i] == ppid) {
+ swap(pids, i, next);
+ swap(ppids, i, next);
+ next++;
+ }
+ }
+ ppid = pids[++count]; // pick up the next pid to scan for
+ } while (count < next);
+
+ return Arrays.stream(pids, 0, count).mapToObj((id) -> new ProcessHandleImpl(id));
+ }
+
+ // Swap two elements in an array
+ private static void swap(long[] array, int x, int y) {
+ long v = array[x];
+ array[x] = array[y];
+ array[y] = v;
+ }
+
+ @Override
+ public ProcessHandle.Info info() {
+ return ProcessHandleImpl.Info.info(pid);
+ }
+
+ @Override
+ public int compareTo(ProcessHandle other) {
+ return Long.compare(pid, ((ProcessHandleImpl) other).pid);
+ }
+
+ @Override
+ public String toString() {
+ return Long.toString(pid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(pid);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof ProcessHandleImpl) &&
+ (pid == ((ProcessHandleImpl) obj).pid);
+ }
+
+ /**
+ * Implementation of ProcessHandle.Info.
+ * Information snapshot about a process.
+ * The attributes of a process vary by operating system and not available
+ * in all implementations. Additionally, information about other processes
+ * is limited by the operating system privileges of the process making the request.
+ * If a value is not available, either a {@code null} or {@code -1} is stored.
+ * The accessor methods return {@code null} if the value is not available.
+ */
+ static class Info implements ProcessHandle.Info {
+ static {
+ initIDs();
+ }
+
+ /**
+ * Initialization of JNI fieldIDs.
+ */
+ private static native void initIDs();
+
+ /**
+ * Fill in this Info instance with information about the native process.
+ * If values are not available the native code does not modify the field.
+ * @param pid of the native process
+ */
+ private native void info0(long pid);
+
+ String command;
+ String[] arguments;
+ long startTime;
+ long totalTime;
+ String user;
+
+ Info() {
+ command = null;
+ arguments = null;
+ startTime = -1L;
+ totalTime = -1L;
+ user = null;
+ }
+
+ /**
+ * Returns the Info object with the fields from the process.
+ * Whatever fields are provided by native are returned.
+ *
+ * @param pid the native process identifier
+ * @return ProcessHandle.Info non-null; individual fields may be null
+ * or -1 if not available.
+ */
+ public static ProcessHandle.Info info(long pid) {
+ Info info = new Info();
+ info.info0(pid);
+ return info;
+ }
+
+ @Override
+ public Optional<String> command() {
+ return Optional.ofNullable(command);
+ }
+
+ @Override
+ public Optional<String[]> arguments() {
+ return Optional.ofNullable(arguments);
+ }
+
+ @Override
+ public Optional<Instant> startInstant() {
+ return (startTime > 0)
+ ? Optional.of(Instant.ofEpochMilli(startTime))
+ : Optional.empty();
+ }
+
+ @Override
+ public Optional<Duration> totalCpuDuration() {
+ return (totalTime != -1)
+ ? Optional.of(Duration.ofNanos(totalTime))
+ : Optional.empty();
+ }
+
+ @Override
+ public Optional<String> user() {
+ return Optional.ofNullable(user);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(60);
+ sb.append('[');
+ if (user != null) {
+ sb.append("user: ");
+ sb.append(user());
+ }
+ if (command != null) {
+ if (sb.length() != 0) sb.append(", ");
+ sb.append("cmd: ");
+ sb.append(command);
+ }
+ if (arguments != null && arguments.length > 0) {
+ if (sb.length() != 0) sb.append(", ");
+ sb.append("args: ");
+ sb.append(Arrays.toString(arguments));
+ }
+ if (startTime != -1) {
+ if (sb.length() != 0) sb.append(", ");
+ sb.append("startTime: ");
+ sb.append(startInstant());
+ }
+ if (totalTime != -1) {
+ if (sb.length() != 0) sb.append(", ");
+ sb.append("totalTime: ");
+ sb.append(totalCpuDuration().toString());
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+ }
+}
--- a/jdk/src/java.base/share/classes/java/lang/RuntimePermission.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/RuntimePermission.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -333,6 +333,12 @@
* "../../../technotes/guides/plugin/developer_guide/rsa_how.html#use">
* usePolicy Permission</a>.</td>
* </tr>
+ * <tr>
+ * <td>manageProcess</td>
+ * <td>Native process termination and information about processes
+ * {@link ProcessHandle}.</td>
+ * <td>Allows code to identify and terminate processes that it did not create.</td>
+ * </tr>
*
* <tr>
* <td>localeServiceProvider</td>
--- a/jdk/src/java.base/share/classes/java/lang/String.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/String.java Thu Jun 04 18:49:37 2015 -0700
@@ -2247,8 +2247,29 @@
* @since 1.5
*/
public String replace(CharSequence target, CharSequence replacement) {
- return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
- this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
+ String starget = target.toString();
+ String srepl = replacement.toString();
+ int j = indexOf(starget);
+ if (j < 0) {
+ return this;
+ }
+ int targLen = starget.length();
+ int targLen1 = Math.max(targLen, 1);
+ final char[] value = this.value;
+ final char[] replValue = srepl.value;
+ int newLenHint = value.length - targLen + replValue.length;
+ if (newLenHint < 0) {
+ throw new OutOfMemoryError();
+ }
+ StringBuilder sb = new StringBuilder(newLenHint);
+ int i = 0;
+ do {
+ sb.append(value, i, j - i)
+ .append(replValue);
+ i = j + targLen;
+ } while (j < value.length && (j = indexOf(starget, j + targLen1)) > 0);
+
+ return sb.append(value, i, value.length - i).toString();
}
/**
--- a/jdk/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Thu Jun 04 18:49:37 2015 -0700
@@ -312,11 +312,16 @@
ret = socketGetOption(opt, null);
return ret;
case IP_TOS:
- ret = socketGetOption(opt, null);
- if (ret == -1) { // ipv6 tos
- return trafficClass;
- } else {
- return ret;
+ try {
+ ret = socketGetOption(opt, null);
+ if (ret == -1) { // ipv6 tos
+ return trafficClass;
+ } else {
+ return ret;
+ }
+ } catch (SocketException se) {
+ // TODO - should make better effort to read TOS or TCLASS
+ return trafficClass; // ipv6 tos
}
case SO_KEEPALIVE:
ret = socketGetOption(opt, null);
--- a/jdk/src/java.base/share/classes/java/net/DatagramSocket.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/net/DatagramSocket.java Thu Jun 04 18:49:37 2015 -0700
@@ -1184,7 +1184,14 @@
if (isClosed())
throw new SocketException("Socket is closed");
- getImpl().setOption(SocketOptions.IP_TOS, tc);
+ try {
+ getImpl().setOption(SocketOptions.IP_TOS, tc);
+ } catch (SocketException se) {
+ // not supported if socket already connected
+ // Solaris returns error in such cases
+ if(!isConnected())
+ throw se;
+ }
}
/**
--- a/jdk/src/java.base/share/classes/java/net/Socket.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/net/Socket.java Thu Jun 04 18:49:37 2015 -0700
@@ -1380,7 +1380,14 @@
if (isClosed())
throw new SocketException("Socket is closed");
- getImpl().setOption(SocketOptions.IP_TOS, tc);
+ try {
+ getImpl().setOption(SocketOptions.IP_TOS, tc);
+ } catch (SocketException se) {
+ // not supported if socket already connected
+ // Solaris returns error in such cases
+ if(!isConnected())
+ throw se;
+ }
}
/**
--- a/jdk/src/java.base/share/classes/java/time/zone/ZoneOffsetTransition.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/time/zone/ZoneOffsetTransition.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -156,6 +156,7 @@
* @param offsetAfter the offset at and after the transition, not null
*/
ZoneOffsetTransition(LocalDateTime transition, ZoneOffset offsetBefore, ZoneOffset offsetAfter) {
+ assert transition.getNano() == 0;
this.epochSecond = transition.toEpochSecond(offsetBefore);
this.transition = transition;
this.offsetBefore = offsetBefore;
@@ -250,7 +251,7 @@
* @return the transition instant, not null
*/
public Instant getInstant() {
- return transition.toInstant(offsetBefore);
+ return Instant.ofEpochSecond(epochSecond);
}
/**
@@ -403,13 +404,7 @@
*/
@Override
public int compareTo(ZoneOffsetTransition transition) {
- if (epochSecond < transition.epochSecond) {
- return -1;
- } else if (epochSecond > transition.epochSecond) {
- return 1;
- } else {
- return this.getInstant().compareTo(transition.getInstant());
- }
+ return Long.compare(epochSecond, transition.epochSecond);
}
//-----------------------------------------------------------------------
@@ -429,7 +424,6 @@
if (other instanceof ZoneOffsetTransition) {
ZoneOffsetTransition d = (ZoneOffsetTransition) other;
return epochSecond == d.epochSecond &&
- transition.equals(d.transition) &&
offsetBefore.equals(d.offsetBefore) && offsetAfter.equals(d.offsetAfter);
}
return false;
--- a/jdk/src/java.base/share/classes/java/time/zone/ZoneOffsetTransitionRule.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/time/zone/ZoneOffsetTransitionRule.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -167,6 +167,7 @@
* @return the rule, not null
* @throws IllegalArgumentException if the day of month indicator is invalid
* @throws IllegalArgumentException if the end of day flag is true when the time is not midnight
+ * @throws IllegalArgumentException if {@code time.getNano()} returns non-zero value
*/
public static ZoneOffsetTransitionRule of(
Month month,
@@ -190,6 +191,9 @@
if (timeEndOfDay && time.equals(LocalTime.MIDNIGHT) == false) {
throw new IllegalArgumentException("Time must be midnight when end of day flag is true");
}
+ if (time.getNano() != 0) {
+ throw new IllegalArgumentException("Time's nano-of-second must be zero");
+ }
return new ZoneOffsetTransitionRule(month, dayOfMonthIndicator, dayOfWeek, time, timeEndOfDay, timeDefnition, standardOffset, offsetBefore, offsetAfter);
}
@@ -220,6 +224,7 @@
ZoneOffset standardOffset,
ZoneOffset offsetBefore,
ZoneOffset offsetAfter) {
+ assert time.getNano() == 0;
this.month = month;
this.dom = (byte) dayOfMonthIndicator;
this.dow = dayOfWeek;
--- a/jdk/src/java.base/share/classes/java/util/Enumeration.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/util/Enumeration.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -40,11 +40,14 @@
* vector, the keys of a hashtable, and the values in a hashtable.
* Enumerations are also used to specify the input streams to a
* <code>SequenceInputStream</code>.
- * <p>
- * NOTE: The functionality of this interface is duplicated by the Iterator
- * interface. In addition, Iterator adds an optional remove operation, and
- * has shorter method names. New implementations should consider using
- * Iterator in preference to Enumeration.
+ *
+ * @apiNote
+ * The functionality of this interface is duplicated by the {@link Iterator}
+ * interface. In addition, {@code Iterator} adds an optional remove operation,
+ * and has shorter method names. New implementations should consider using
+ * {@code Iterator} in preference to {@code Enumeration}. It is possible to
+ * adapt an {@code Enumeration} to an {@code Iterator} by using the
+ * {@link #asIterator} method.
*
* @see java.util.Iterator
* @see java.io.SequenceInputStream
@@ -76,4 +79,49 @@
* @exception NoSuchElementException if no more elements exist.
*/
E nextElement();
+
+ /**
+ * Returns an {@link Iterator} that traverses the remaining elements
+ * covered by this enumeration. Traversal is undefined if any methods
+ * are called on this enumeration after the call to {@code asIterator}.
+ *
+ * @apiNote
+ * This method is intended to help adapt code that produces
+ * {@code Enumeration} instances to code that consumes {@code Iterator}
+ * instances. For example, the {@link java.util.jar.JarFile#entries
+ * JarFile.entries()} method returns an {@code Enumeration<JarEntry>}.
+ * This can be turned into an {@code Iterator}, and then the
+ * {@code forEachRemaining()} method can be used:
+ *
+ * <pre>{@code
+ * JarFile jarFile = ... ;
+ * jarFile.entries().asIterator().forEachRemaining(entry -> { ... });
+ * }</pre>
+ *
+ * (Note that there is also a {@link java.util.jar.JarFile#stream
+ * JarFile.stream()} method that returns a {@code Stream} of entries,
+ * which may be more convenient in some cases.)
+ *
+ * @implSpec
+ * The default implementation returns an {@code Iterator} whose
+ * {@link Iterator#hasNext hasNext} method calls this Enumeration's
+ * {@code hasMoreElements} method, whose {@link Iterator#next next}
+ * method calls this Enumeration's {@code nextElement} method, and
+ * whose {@link Iterator#remove remove} method throws
+ * {@code UnsupportedOperationException}.
+ *
+ * @return an Iterator representing the remaining elements of this Enumeration
+ *
+ * @since 1.9
+ */
+ default Iterator<E> asIterator() {
+ return new Iterator<>() {
+ @Override public boolean hasNext() {
+ return hasMoreElements();
+ }
+ @Override public E next() {
+ return nextElement();
+ }
+ };
+ }
}
--- a/jdk/src/java.base/share/classes/java/util/Iterator.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/util/Iterator.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -43,6 +43,10 @@
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
* Java Collections Framework</a>.
*
+ * @apiNote
+ * An {@link Enumeration} can be converted into an {@code Iterator} by
+ * using the {@link Enumeration#asIterator} method.
+ *
* @param <E> the type of elements returned by this iterator
*
* @author Josh Bloch
--- a/jdk/src/java.base/share/classes/javax/net/ssl/ExtendedSSLSession.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/ExtendedSSLSession.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,7 @@
import java.util.List;
/**
- * Extends the <code>SSLSession</code> interface to support additional
+ * Extends the {@code SSLSession} interface to support additional
* session attributes.
*
* @since 1.7
@@ -39,8 +39,8 @@
* is willing to use.
* <p>
* Note: this method is used to indicate to the peer which signature
- * algorithms may be used for digital signatures in TLS 1.2. It is
- * not meaningful for TLS versions prior to 1.2.
+ * algorithms may be used for digital signatures in TLS/DTLS 1.2. It is
+ * not meaningful for TLS/DTLS versions prior to 1.2.
* <p>
* The signature algorithm name must be a standard Java Security
* name (such as "SHA1withRSA", "SHA256withECDSA", and so on).
@@ -52,7 +52,7 @@
* Note: the local supported signature algorithms should conform to
* the algorithm constraints specified by
* {@link SSLParameters#getAlgorithmConstraints getAlgorithmConstraints()}
- * method in <code>SSLParameters</code>.
+ * method in {@code SSLParameters}.
*
* @return An array of supported signature algorithms, in descending
* order of preference. The return value is an empty array if
@@ -67,8 +67,8 @@
* able to use.
* <p>
* Note: this method is used to indicate to the local side which signature
- * algorithms may be used for digital signatures in TLS 1.2. It is
- * not meaningful for TLS versions prior to 1.2.
+ * algorithms may be used for digital signatures in TLS/DTLS 1.2. It is
+ * not meaningful for TLS/DTLS versions prior to 1.2.
* <p>
* The signature algorithm name must be a standard Java Security
* name (such as "SHA1withRSA", "SHA256withECDSA", and so on).
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SNIServerName.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SNIServerName.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,7 +31,7 @@
* Instances of this class represent a server name in a Server Name
* Indication (SNI) extension.
* <P>
- * The SNI extension is a feature that extends the SSL/TLS protocols to
+ * The SNI extension is a feature that extends the SSL/TLS/DTLS protocols to
* indicate what server name the client is attempting to connect to during
* handshaking. See section 3, "Server Name Indication", of <A
* HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions (RFC 6066)</A>.
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLContext.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLContext.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,12 +32,12 @@
/**
* Instances of this class represent a secure socket protocol
* implementation which acts as a factory for secure socket
- * factories or <code>SSLEngine</code>s. This class is initialized
+ * factories or {@code SSLEngine}s. This class is initialized
* with an optional set of key and trust managers and source of
* secure random bytes.
*
* <p> Every implementation of the Java platform is required to support the
- * following standard <code>SSLContext</code> protocol:
+ * following standard {@code SSLContext} protocol:
* <ul>
* <li><tt>TLSv1</tt></li>
* </ul>
@@ -79,7 +79,7 @@
* <p>If a default context was set using the {@link #setDefault
* SSLContext.setDefault()} method, it is returned. Otherwise, the first
* call of this method triggers the call
- * <code>SSLContext.getInstance("Default")</code>.
+ * {@code SSLContext.getInstance("Default")}.
* If successful, that object is made the default SSL context and returned.
*
* <p>The default context is immediately
@@ -106,8 +106,8 @@
* @param context the SSLContext
* @throws NullPointerException if context is null
* @throws SecurityException if a security manager exists and its
- * <code>checkPermission</code> method does not allow
- * <code>SSLPermission("setDefaultSSLContext")</code>
+ * {@code checkPermission} method does not allow
+ * {@code SSLPermission("setDefaultSSLContext")}
* @since 1.6
*/
public static synchronized void setDefault(SSLContext context) {
@@ -122,7 +122,7 @@
}
/**
- * Returns a <code>SSLContext</code> object that implements the
+ * Returns a {@code SSLContext} object that implements the
* specified secure socket protocol.
*
* <p> This method traverses the list of registered security Providers,
@@ -141,7 +141,7 @@
* Documentation</a>
* for information about standard protocol names.
*
- * @return the new <code>SSLContext</code> object.
+ * @return the new {@code SSLContext} object.
*
* @exception NoSuchAlgorithmException if no Provider supports a
* SSLContextSpi implementation for the
@@ -159,7 +159,7 @@
}
/**
- * Returns a <code>SSLContext</code> object that implements the
+ * Returns a {@code SSLContext} object that implements the
* specified secure socket protocol.
*
* <p> A new SSLContext object encapsulating the
@@ -179,7 +179,7 @@
*
* @param provider the name of the provider.
*
- * @return the new <code>SSLContext</code> object.
+ * @return the new {@code SSLContext} object.
*
* @throws NoSuchAlgorithmException if a SSLContextSpi
* implementation for the specified protocol is not
@@ -202,7 +202,7 @@
}
/**
- * Returns a <code>SSLContext</code> object that implements the
+ * Returns a {@code SSLContext} object that implements the
* specified secure socket protocol.
*
* <p> A new SSLContext object encapsulating the
@@ -219,7 +219,7 @@
*
* @param provider an instance of the provider.
*
- * @return the new <code>SSLContext</code> object.
+ * @return the new {@code SSLContext} object.
*
* @throws NoSuchAlgorithmException if a SSLContextSpi
* implementation for the specified protocol is not available
@@ -239,22 +239,22 @@
}
/**
- * Returns the protocol name of this <code>SSLContext</code> object.
+ * Returns the protocol name of this {@code SSLContext} object.
*
* <p>This is the same name that was specified in one of the
- * <code>getInstance</code> calls that created this
- * <code>SSLContext</code> object.
+ * {@code getInstance} calls that created this
+ * {@code SSLContext} object.
*
- * @return the protocol name of this <code>SSLContext</code> object.
+ * @return the protocol name of this {@code SSLContext} object.
*/
public final String getProtocol() {
return this.protocol;
}
/**
- * Returns the provider of this <code>SSLContext</code> object.
+ * Returns the provider of this {@code SSLContext} object.
*
- * @return the provider of this <code>SSLContext</code> object
+ * @return the provider of this {@code SSLContext} object
*/
public final Provider getProvider() {
return this.provider;
@@ -283,31 +283,35 @@
}
/**
- * Returns a <code>SocketFactory</code> object for this
+ * Returns a {@code SocketFactory} object for this
* context.
*
- * @return the <code>SocketFactory</code> object
+ * @return the {@code SocketFactory} object
+ * @throws UnsupportedOperationException if the underlying provider
+ * does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires
- * initialization and the <code>init()</code> has not been called
+ * initialization and the {@code init()} has not been called
*/
public final SSLSocketFactory getSocketFactory() {
return contextSpi.engineGetSocketFactory();
}
/**
- * Returns a <code>ServerSocketFactory</code> object for
+ * Returns a {@code ServerSocketFactory} object for
* this context.
*
- * @return the <code>ServerSocketFactory</code> object
+ * @return the {@code ServerSocketFactory} object
+ * @throws UnsupportedOperationException if the underlying provider
+ * does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires
- * initialization and the <code>init()</code> has not been called
+ * initialization and the {@code init()} has not been called
*/
public final SSLServerSocketFactory getServerSocketFactory() {
return contextSpi.engineGetServerSocketFactory();
}
/**
- * Creates a new <code>SSLEngine</code> using this context.
+ * Creates a new {@code SSLEngine} using this context.
* <P>
* Applications using this factory method are providing no hints
* for an internal session reuse strategy. If hints are desired,
@@ -317,11 +321,11 @@
* Some cipher suites (such as Kerberos) require remote hostname
* information, in which case this factory method should not be used.
*
- * @return the <code>SSLEngine</code> object
+ * @return the {@code SSLEngine} object
* @throws UnsupportedOperationException if the underlying provider
* does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires
- * initialization and the <code>init()</code> has not been called
+ * initialization and the {@code init()} has not been called
* @since 1.5
*/
public final SSLEngine createSSLEngine() {
@@ -338,7 +342,7 @@
}
/**
- * Creates a new <code>SSLEngine</code> using this context using
+ * Creates a new {@code SSLEngine} using this context using
* advisory peer information.
* <P>
* Applications using this factory method are providing hints
@@ -349,11 +353,11 @@
*
* @param peerHost the non-authoritative name of the host
* @param peerPort the non-authoritative port
- * @return the new <code>SSLEngine</code> object
+ * @return the new {@code SSLEngine} object
* @throws UnsupportedOperationException if the underlying provider
* does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires
- * initialization and the <code>init()</code> has not been called
+ * initialization and the {@code init()} has not been called
* @since 1.5
*/
public final SSLEngine createSSLEngine(String peerHost, int peerPort) {
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLContextSpi.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLContextSpi.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,7 +29,7 @@
/**
* This class defines the <i>Service Provider Interface</i> (<b>SPI</b>)
- * for the <code>SSLContext</code> class.
+ * for the {@code SSLContext} class.
*
* <p> All the abstract methods in this class must be implemented by each
* cryptographic service provider who wishes to supply the implementation
@@ -52,31 +52,35 @@
SecureRandom sr) throws KeyManagementException;
/**
- * Returns a <code>SocketFactory</code> object for this
+ * Returns a {@code SocketFactory} object for this
* context.
*
- * @return the <code>SocketFactory</code> object
+ * @return the {@code SocketFactory} object
+ * @throws UnsupportedOperationException if the underlying provider
+ * does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires
- * initialization and the <code>engineInit()</code>
+ * initialization and the {@code engineInit()}
* has not been called
* @see javax.net.ssl.SSLContext#getSocketFactory()
*/
protected abstract SSLSocketFactory engineGetSocketFactory();
/**
- * Returns a <code>ServerSocketFactory</code> object for
+ * Returns a {@code ServerSocketFactory} object for
* this context.
*
- * @return the <code>ServerSocketFactory</code> object
+ * @return the {@code ServerSocketFactory} object
+ * @throws UnsupportedOperationException if the underlying provider
+ * does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires
- * initialization and the <code>engineInit()</code>
+ * initialization and the {@code engineInit()}
* has not been called
* @see javax.net.ssl.SSLContext#getServerSocketFactory()
*/
protected abstract SSLServerSocketFactory engineGetServerSocketFactory();
/**
- * Creates a new <code>SSLEngine</code> using this context.
+ * Creates a new {@code SSLEngine} using this context.
* <P>
* Applications using this factory method are providing no hints
* for an internal session reuse strategy. If hints are desired,
@@ -86,9 +90,9 @@
* Some cipher suites (such as Kerberos) require remote hostname
* information, in which case this factory method should not be used.
*
- * @return the <code>SSLEngine</code> Object
+ * @return the {@code SSLEngine} Object
* @throws IllegalStateException if the SSLContextImpl requires
- * initialization and the <code>engineInit()</code>
+ * initialization and the {@code engineInit()}
* has not been called
*
* @see SSLContext#createSSLEngine()
@@ -98,7 +102,7 @@
protected abstract SSLEngine engineCreateSSLEngine();
/**
- * Creates a <code>SSLEngine</code> using this context.
+ * Creates a {@code SSLEngine} using this context.
* <P>
* Applications using this factory method are providing hints
* for an internal session reuse strategy.
@@ -108,9 +112,9 @@
*
* @param host the non-authoritative name of the host
* @param port the non-authoritative port
- * @return the <code>SSLEngine</code> Object
+ * @return the {@code SSLEngine} Object
* @throws IllegalStateException if the SSLContextImpl requires
- * initialization and the <code>engineInit()</code>
+ * initialization and the {@code engineInit()}
* has not been called
*
* @see SSLContext#createSSLEngine(String, int)
@@ -120,19 +124,19 @@
protected abstract SSLEngine engineCreateSSLEngine(String host, int port);
/**
- * Returns a server <code>SSLSessionContext</code> object for
+ * Returns a server {@code SSLSessionContext} object for
* this context.
*
- * @return the <code>SSLSessionContext</code> object
+ * @return the {@code SSLSessionContext} object
* @see javax.net.ssl.SSLContext#getServerSessionContext()
*/
protected abstract SSLSessionContext engineGetServerSessionContext();
/**
- * Returns a client <code>SSLSessionContext</code> object for
+ * Returns a client {@code SSLSessionContext} object for
* this context.
*
- * @return the <code>SSLSessionContext</code> object
+ * @return the {@code SSLSessionContext} object
* @see javax.net.ssl.SSLContext#getClientSessionContext()
*/
protected abstract SSLSessionContext engineGetClientSessionContext();
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,15 +37,15 @@
* <P>
* The secure communications modes include: <UL>
*
- * <LI> <em>Integrity Protection</em>. SSL/TLS protects against
+ * <LI> <em>Integrity Protection</em>. SSL/TLS/DTLS protects against
* modification of messages by an active wiretapper.
*
- * <LI> <em>Authentication</em>. In most modes, SSL/TLS provides
+ * <LI> <em>Authentication</em>. In most modes, SSL/TLS/DTLS provides
* peer authentication. Servers are usually authenticated, and
* clients may be authenticated as requested by servers.
*
* <LI> <em>Confidentiality (Privacy Protection)</em>. In most
- * modes, SSL/TLS encrypts data being sent between client and
+ * modes, SSL/TLS/DTLS encrypts data being sent between client and
* server. This protects the confidentiality of data, so that
* passive wiretappers won't see sensitive data such as financial
* information or personal information of many kinds.
@@ -65,19 +65,19 @@
* handshaking has completed, you can access session attributes by
* using the {@link #getSession()} method.
* <P>
- * The <code>SSLSocket</code> class provides much of the same security
+ * The {@code SSLSocket} class provides much of the same security
* functionality, but all of the inbound and outbound data is
* automatically transported using the underlying {@link
* java.net.Socket Socket}, which by design uses a blocking model.
* While this is appropriate for many applications, this model does not
* provide the scalability required by large servers.
* <P>
- * The primary distinction of an <code>SSLEngine</code> is that it
+ * The primary distinction of an {@code SSLEngine} is that it
* operates on inbound and outbound byte streams, independent of the
* transport mechanism. It is the responsibility of the
- * <code>SSLEngine</code> user to arrange for reliable I/O transport to
- * the peer. By separating the SSL/TLS abstraction from the I/O
- * transport mechanism, the <code>SSLEngine</code> can be used for a
+ * {@code SSLEngine} user to arrange for reliable I/O transport to
+ * the peer. By separating the SSL/TLS/DTLS abstraction from the I/O
+ * transport mechanism, the {@code SSLEngine} can be used for a
* wide variety of I/O types, such as {@link
* java.nio.channels.spi.AbstractSelectableChannel#configureBlocking(boolean)
* non-blocking I/O (polling)}, {@link java.nio.channels.Selector
@@ -87,7 +87,7 @@
* HREF="http://www.jcp.org/en/jsr/detail?id=203"> future asynchronous
* I/O models </A>, and so on.
* <P>
- * At a high level, the <code>SSLEngine</code> appears thus:
+ * At a high level, the {@code SSLEngine} appears thus:
*
* <pre>
* app data
@@ -115,18 +115,18 @@
* mechanism. Inbound data is data which has been received from the
* peer, and outbound data is destined for the peer.
* <P>
- * (In the context of an <code>SSLEngine</code>, the term "handshake
+ * (In the context of an {@code SSLEngine}, the term "handshake
* data" is taken to mean any data exchanged to establish and control a
- * secure connection. Handshake data includes the SSL/TLS messages
+ * secure connection. Handshake data includes the SSL/TLS/DTLS messages
* "alert", "change_cipher_spec," and "handshake.")
* <P>
- * There are five distinct phases to an <code>SSLEngine</code>.
+ * There are five distinct phases to an {@code SSLEngine}.
*
* <OL>
- * <li> Creation - The <code>SSLEngine</code> has been created and
+ * <li> Creation - The {@code SSLEngine} has been created and
* initialized, but has not yet been used. During this phase, an
- * application may set any <code>SSLEngine</code>-specific settings
- * (enabled cipher suites, whether the <code>SSLEngine</code> should
+ * application may set any {@code SSLEngine}-specific settings
+ * (enabled cipher suites, whether the {@code SSLEngine} should
* handshake in client or server mode, and so on). Once
* handshaking has begun, though, any new settings (except
* client/server mode, see below) will be used for
@@ -139,7 +139,7 @@
*
* <li> Application Data - Once the communication parameters have
* been established and the handshake is complete, application data
- * may flow through the <code>SSLEngine</code>. Outbound
+ * may flow through the {@code SSLEngine}. Outbound
* application messages are encrypted and integrity protected,
* and inbound messages reverse the process.
*
@@ -147,50 +147,50 @@
* the session at any time during the Application Data phase. New
* handshaking data can be intermixed among the application data.
* Before starting the rehandshake phase, the application may
- * reset the SSL/TLS communication parameters such as the list of
+ * reset the SSL/TLS/DTLS communication parameters such as the list of
* enabled ciphersuites and whether to use client authentication,
* but can not change between client/server modes. As before, once
- * handshaking has begun, any new <code>SSLEngine</code>
+ * handshaking has begun, any new {@code SSLEngine}
* configuration settings will not be used until the next
* handshake.
*
* <li> Closure - When the connection is no longer needed, the
- * application should close the <code>SSLEngine</code> and should
+ * application should close the {@code SSLEngine} and should
* send/receive any remaining messages to the peer before
* closing the underlying transport mechanism. Once an engine is
- * closed, it is not reusable: a new <code>SSLEngine</code> must
+ * closed, it is not reusable: a new {@code SSLEngine} must
* be created.
* </OL>
- * An <code>SSLEngine</code> is created by calling {@link
+ * An {@code SSLEngine} is created by calling {@link
* SSLContext#createSSLEngine()} from an initialized
- * <code>SSLContext</code>. Any configuration
+ * {@code SSLContext}. Any configuration
* parameters should be set before making the first call to
- * <code>wrap()</code>, <code>unwrap()</code>, or
- * <code>beginHandshake()</code>. These methods all trigger the
+ * {@code wrap()}, {@code unwrap()}, or
+ * {@code beginHandshake()}. These methods all trigger the
* initial handshake.
* <P>
* Data moves through the engine by calling {@link #wrap(ByteBuffer,
* ByteBuffer) wrap()} or {@link #unwrap(ByteBuffer, ByteBuffer)
* unwrap()} on outbound or inbound data, respectively. Depending on
- * the state of the <code>SSLEngine</code>, a <code>wrap()</code> call
+ * the state of the {@code SSLEngine}, a {@code wrap()} call
* may consume application data from the source buffer and may produce
* network data in the destination buffer. The outbound data
* may contain application and/or handshake data. A call to
- * <code>unwrap()</code> will examine the source buffer and may
+ * {@code unwrap()} will examine the source buffer and may
* advance the handshake if the data is handshaking information, or
* may place application data in the destination buffer if the data
- * is application. The state of the underlying SSL/TLS algorithm
+ * is application. The state of the underlying SSL/TLS/DTLS algorithm
* will determine when data is consumed and produced.
* <P>
- * Calls to <code>wrap()</code> and <code>unwrap()</code> return an
- * <code>SSLEngineResult</code> which indicates the status of the
+ * Calls to {@code wrap()} and {@code unwrap()} return an
+ * {@code SSLEngineResult} which indicates the status of the
* operation, and (optionally) how to interact with the engine to make
* progress.
* <P>
- * The <code>SSLEngine</code> produces/consumes complete SSL/TLS
+ * The {@code SSLEngine} produces/consumes complete SSL/TLS/DTLS
* packets only, and does not store application data internally between
- * calls to <code>wrap()/unwrap()</code>. Thus input and output
- * <code>ByteBuffer</code>s must be sized appropriately to hold the
+ * calls to {@code wrap()/unwrap()}. Thus input and output
+ * {@code ByteBuffer}s must be sized appropriately to hold the
* maximum record that can be produced. Calls to {@link
* SSLSession#getPacketBufferSize()} and {@link
* SSLSession#getApplicationBufferSize()} should be used to determine
@@ -200,12 +200,12 @@
* must determine (via {@link SSLEngineResult}) and correct the
* problem, and then try the call again.
* <P>
- * For example, <code>unwrap()</code> will return a {@link
+ * For example, {@code unwrap()} will return a {@link
* SSLEngineResult.Status#BUFFER_OVERFLOW} result if the engine
* determines that there is not enough destination buffer space available.
* Applications should call {@link SSLSession#getApplicationBufferSize()}
* and compare that value with the space available in the destination buffer,
- * enlarging the buffer if necessary. Similarly, if <code>unwrap()</code>
+ * enlarging the buffer if necessary. Similarly, if {@code unwrap()}
* were to return a {@link SSLEngineResult.Status#BUFFER_UNDERFLOW}, the
* application should call {@link SSLSession#getPacketBufferSize()} to ensure
* that the source buffer has enough room to hold a record (enlarging if
@@ -241,8 +241,8 @@
* }</pre>
*
* <P>
- * Unlike <code>SSLSocket</code>, all methods of SSLEngine are
- * non-blocking. <code>SSLEngine</code> implementations may
+ * Unlike {@code SSLSocket}, all methods of SSLEngine are
+ * non-blocking. {@code SSLEngine} implementations may
* require the results of tasks that may take an extended period of
* time to complete, or may even block. For example, a TrustManager
* may need to connect to a remote certificate validation service,
@@ -252,8 +252,8 @@
* seemingly blocking.
* <P>
* For any operation which may potentially block, the
- * <code>SSLEngine</code> will create a {@link java.lang.Runnable}
- * delegated task. When <code>SSLEngineResult</code> indicates that a
+ * {@code SSLEngine} will create a {@link java.lang.Runnable}
+ * delegated task. When {@code SSLEngineResult} indicates that a
* delegated task result is needed, the application must call {@link
* #getDelegatedTask()} to obtain an outstanding delegated task and
* call its {@link java.lang.Runnable#run() run()} method (possibly using
@@ -262,16 +262,16 @@
* exist, and try the original operation again.
* <P>
* At the end of a communication session, applications should properly
- * close the SSL/TLS link. The SSL/TLS protocols have closure handshake
- * messages, and these messages should be communicated to the peer
- * before releasing the <code>SSLEngine</code> and closing the
+ * close the SSL/TLS/DTLS link. The SSL/TLS/DTLS protocols have closure
+ * handshake messages, and these messages should be communicated to the
+ * peer before releasing the {@code SSLEngine} and closing the
* underlying transport mechanism. A close can be initiated by one of:
* an SSLException, an inbound closure handshake message, or one of the
* close methods. In all cases, closure handshake messages are
- * generated by the engine, and <code>wrap()</code> should be repeatedly
- * called until the resulting <code>SSLEngineResult</code>'s status
+ * generated by the engine, and {@code wrap()} should be repeatedly
+ * called until the resulting {@code SSLEngineResult}'s status
* returns "CLOSED", or {@link #isOutboundDone()} returns true. All
- * data obtained from the <code>wrap()</code> method should be sent to the
+ * data obtained from the {@code wrap()} method should be sent to the
* peer.
* <P>
* {@link #closeOutbound()} is used to signal the engine that the
@@ -279,12 +279,12 @@
* <P>
* A peer will signal its intent to close by sending its own closure
* handshake message. After this message has been received and
- * processed by the local <code>SSLEngine</code>'s <code>unwrap()</code>
+ * processed by the local {@code SSLEngine}'s {@code unwrap()}
* call, the application can detect the close by calling
- * <code>unwrap()</code> and looking for a <code>SSLEngineResult</code>
+ * {@code unwrap()} and looking for a {@code SSLEngineResult}
* with status "CLOSED", or if {@link #isInboundDone()} returns true.
* If for some reason the peer closes the communication link without
- * sending the proper SSL/TLS closure message, the application can
+ * sending the proper SSL/TLS/DTLS closure message, the application can
* detect the end-of-stream and can signal the engine via {@link
* #closeInbound()} that there will no more inbound messages to
* process. Some applications might choose to require orderly shutdown
@@ -315,16 +315,16 @@
* and/or non-private (unencrypted) communications will such a
* cipher suite be selected.
* <P>
- * Each SSL/TLS connection must have one client and one server, thus
+ * Each SSL/TLS/DTLS connection must have one client and one server, thus
* each endpoint must decide which role to assume. This choice determines
* who begins the handshaking process as well as which type of messages
* should be sent by each party. The method {@link
* #setUseClientMode(boolean)} configures the mode. Once the initial
- * handshaking has started, an <code>SSLEngine</code> can not switch
+ * handshaking has started, an {@code SSLEngine} can not switch
* between client and server modes, even when performing renegotiations.
* <P>
* Applications might choose to process delegated tasks in different
- * threads. When an <code>SSLEngine</code>
+ * threads. When an {@code SSLEngine}
* is created, the current {@link java.security.AccessControlContext}
* is saved. All future delegated tasks will be processed using this
* context: that is, all access control decisions will be made using the
@@ -336,10 +336,10 @@
* There are two concurrency issues to be aware of:
*
* <OL>
- * <li>The <code>wrap()</code> and <code>unwrap()</code> methods
+ * <li>The {@code wrap()} and {@code unwrap()} methods
* may execute concurrently of each other.
*
- * <li> The SSL/TLS protocols employ ordered packets.
+ * <li> The SSL/TLS/DTLS protocols employ ordered packets.
* Applications must take care to ensure that generated packets
* are delivered in sequence. If packets arrive
* out-of-order, unexpected or fatal results may occur.
@@ -354,7 +354,7 @@
* </pre>
*
* As a corollary, two threads must not attempt to call the same method
- * (either <code>wrap()</code> or <code>unwrap()</code>) concurrently,
+ * (either {@code wrap()} or {@code unwrap()}) concurrently,
* because there is no way to guarantee the eventual packet ordering.
* </OL>
*
@@ -374,7 +374,7 @@
private int peerPort = -1;
/**
- * Constructor for an <code>SSLEngine</code> providing no hints
+ * Constructor for an {@code SSLEngine} providing no hints
* for an internal session reuse strategy.
*
* @see SSLContext#createSSLEngine()
@@ -384,10 +384,10 @@
}
/**
- * Constructor for an <code>SSLEngine</code>.
+ * Constructor for an {@code SSLEngine}.
* <P>
- * <code>SSLEngine</code> implementations may use the
- * <code>peerHost</code> and <code>peerPort</code> parameters as hints
+ * {@code SSLEngine} implementations may use the
+ * {@code peerHost} and {@code peerPort} parameters as hints
* for their internal session reuse strategy.
* <P>
* Some cipher suites (such as Kerberos) require remote hostname
@@ -395,7 +395,7 @@
* constructor to use Kerberos.
* <P>
* The parameters are not authenticated by the
- * <code>SSLEngine</code>.
+ * {@code SSLEngine}.
*
* @param peerHost the name of the peer host
* @param peerPort the port number of the peer
@@ -435,7 +435,7 @@
/**
* Attempts to encode a buffer of plaintext application data into
- * SSL/TLS network data.
+ * SSL/TLS/DTLS network data.
* <P>
* An invocation of this method behaves in exactly the same manner
* as the invocation:
@@ -445,20 +445,20 @@
* </pre></blockquote>
*
* @param src
- * a <code>ByteBuffer</code> containing outbound application data
+ * a {@code ByteBuffer} containing outbound application data
* @param dst
- * a <code>ByteBuffer</code> to hold outbound network data
- * @return an <code>SSLEngineResult</code> describing the result
+ * a {@code ByteBuffer} to hold outbound network data
+ * @return an {@code SSLEngineResult} describing the result
* of this operation.
* @throws SSLException
* A problem was encountered while processing the
- * data that caused the <code>SSLEngine</code> to abort.
+ * data that caused the {@code SSLEngine} to abort.
* See the class description for more information on
* engine closure.
* @throws ReadOnlyBufferException
- * if the <code>dst</code> buffer is read-only.
+ * if the {@code dst} buffer is read-only.
* @throws IllegalArgumentException
- * if either <code>src</code> or <code>dst</code>
+ * if either {@code src} or {@code dst}
* is null.
* @throws IllegalStateException if the client/server mode
* has not yet been set.
@@ -471,7 +471,7 @@
/**
* Attempts to encode plaintext bytes from a sequence of data
- * buffers into SSL/TLS network data.
+ * buffers into SSL/TLS/DTLS network data.
* <P>
* An invocation of this method behaves in exactly the same manner
* as the invocation:
@@ -481,22 +481,22 @@
* </pre></blockquote>
*
* @param srcs
- * an array of <code>ByteBuffers</code> containing the
+ * an array of {@code ByteBuffers} containing the
* outbound application data
* @param dst
- * a <code>ByteBuffer</code> to hold outbound network data
- * @return an <code>SSLEngineResult</code> describing the result
+ * a {@code ByteBuffer} to hold outbound network data
+ * @return an {@code SSLEngineResult} describing the result
* of this operation.
* @throws SSLException
* A problem was encountered while processing the
- * data that caused the <code>SSLEngine</code> to abort.
+ * data that caused the {@code SSLEngine} to abort.
* See the class description for more information on
* engine closure.
* @throws ReadOnlyBufferException
- * if the <code>dst</code> buffer is read-only.
+ * if the {@code dst} buffer is read-only.
* @throws IllegalArgumentException
- * if either <code>srcs</code> or <code>dst</code>
- * is null, or if any element in <code>srcs</code> is null.
+ * if either {@code srcs} or {@code dst}
+ * is null, or if any element in {@code srcs} is null.
* @throws IllegalStateException if the client/server mode
* has not yet been set.
* @see #wrap(ByteBuffer [], int, int, ByteBuffer)
@@ -512,7 +512,7 @@
/**
* Attempts to encode plaintext bytes from a subsequence of data
- * buffers into SSL/TLS network data. This <i>"gathering"</i>
+ * buffers into SSL/TLS/DTLS network data. This <i>"gathering"</i>
* operation encodes, in a single invocation, a sequence of bytes
* from one or more of a given sequence of buffers. Gathering
* wraps are often useful when implementing network protocols or
@@ -535,49 +535,49 @@
* it was generated. The application must properly synchronize
* multiple calls to this method.
* <P>
- * If this <code>SSLEngine</code> has not yet started its initial
+ * If this {@code SSLEngine} has not yet started its initial
* handshake, this method will automatically start the handshake.
* <P>
- * This method will attempt to produce SSL/TLS records, and will
+ * This method will attempt to produce SSL/TLS/DTLS records, and will
* consume as much source data as possible, but will never consume
* more than the sum of the bytes remaining in each buffer. Each
- * <code>ByteBuffer</code>'s position is updated to reflect the
+ * {@code ByteBuffer}'s position is updated to reflect the
* amount of data consumed or produced. The limits remain the
* same.
* <P>
- * The underlying memory used by the <code>srcs</code> and
- * <code>dst ByteBuffer</code>s must not be the same.
+ * The underlying memory used by the {@code srcs} and
+ * {@code dst ByteBuffer}s must not be the same.
* <P>
* See the class description for more information on engine closure.
*
* @param srcs
- * an array of <code>ByteBuffers</code> containing the
+ * an array of {@code ByteBuffers} containing the
* outbound application data
* @param offset
* The offset within the buffer array of the first buffer from
* which bytes are to be retrieved; it must be non-negative
- * and no larger than <code>srcs.length</code>
+ * and no larger than {@code srcs.length}
* @param length
* The maximum number of buffers to be accessed; it must be
* non-negative and no larger than
- * <code>srcs.length</code> - <code>offset</code>
+ * {@code srcs.length} - {@code offset}
* @param dst
- * a <code>ByteBuffer</code> to hold outbound network data
- * @return an <code>SSLEngineResult</code> describing the result
+ * a {@code ByteBuffer} to hold outbound network data
+ * @return an {@code SSLEngineResult} describing the result
* of this operation.
* @throws SSLException
* A problem was encountered while processing the
- * data that caused the <code>SSLEngine</code> to abort.
+ * data that caused the {@code SSLEngine} to abort.
* See the class description for more information on
* engine closure.
* @throws IndexOutOfBoundsException
- * if the preconditions on the <code>offset</code> and
- * <code>length</code> parameters do not hold.
+ * if the preconditions on the {@code offset} and
+ * {@code length} parameters do not hold.
* @throws ReadOnlyBufferException
- * if the <code>dst</code> buffer is read-only.
+ * if the {@code dst} buffer is read-only.
* @throws IllegalArgumentException
- * if either <code>srcs</code> or <code>dst</code>
- * is null, or if any element in the <code>srcs</code>
+ * if either {@code srcs} or {@code dst}
+ * is null, or if any element in the {@code srcs}
* subsequence specified is null.
* @throws IllegalStateException if the client/server mode
* has not yet been set.
@@ -589,7 +589,7 @@
int length, ByteBuffer dst) throws SSLException;
/**
- * Attempts to decode SSL/TLS network data into a plaintext
+ * Attempts to decode SSL/TLS/DTLS network data into a plaintext
* application data buffer.
* <P>
* An invocation of this method behaves in exactly the same manner
@@ -600,20 +600,20 @@
* </pre></blockquote>
*
* @param src
- * a <code>ByteBuffer</code> containing inbound network data.
+ * a {@code ByteBuffer} containing inbound network data.
* @param dst
- * a <code>ByteBuffer</code> to hold inbound application data.
- * @return an <code>SSLEngineResult</code> describing the result
+ * a {@code ByteBuffer} to hold inbound application data.
+ * @return an {@code SSLEngineResult} describing the result
* of this operation.
* @throws SSLException
* A problem was encountered while processing the
- * data that caused the <code>SSLEngine</code> to abort.
+ * data that caused the {@code SSLEngine} to abort.
* See the class description for more information on
* engine closure.
* @throws ReadOnlyBufferException
- * if the <code>dst</code> buffer is read-only.
+ * if the {@code dst} buffer is read-only.
* @throws IllegalArgumentException
- * if either <code>src</code> or <code>dst</code>
+ * if either {@code src} or {@code dst}
* is null.
* @throws IllegalStateException if the client/server mode
* has not yet been set.
@@ -625,7 +625,7 @@
}
/**
- * Attempts to decode SSL/TLS network data into a sequence of plaintext
+ * Attempts to decode SSL/TLS/DTLS network data into a sequence of plaintext
* application data buffers.
* <P>
* An invocation of this method behaves in exactly the same manner
@@ -636,22 +636,22 @@
* </pre></blockquote>
*
* @param src
- * a <code>ByteBuffer</code> containing inbound network data.
+ * a {@code ByteBuffer} containing inbound network data.
* @param dsts
- * an array of <code>ByteBuffer</code>s to hold inbound
+ * an array of {@code ByteBuffer}s to hold inbound
* application data.
- * @return an <code>SSLEngineResult</code> describing the result
+ * @return an {@code SSLEngineResult} describing the result
* of this operation.
* @throws SSLException
* A problem was encountered while processing the
- * data that caused the <code>SSLEngine</code> to abort.
+ * data that caused the {@code SSLEngine} to abort.
* See the class description for more information on
* engine closure.
* @throws ReadOnlyBufferException
- * if any of the <code>dst</code> buffers are read-only.
+ * if any of the {@code dst} buffers are read-only.
* @throws IllegalArgumentException
- * if either <code>src</code> or <code>dsts</code>
- * is null, or if any element in <code>dsts</code> is null.
+ * if either {@code src} or {@code dsts}
+ * is null, or if any element in {@code dsts} is null.
* @throws IllegalStateException if the client/server mode
* has not yet been set.
* @see #unwrap(ByteBuffer, ByteBuffer [], int, int)
@@ -665,7 +665,7 @@
}
/**
- * Attempts to decode SSL/TLS network data into a subsequence of
+ * Attempts to decode SSL/TLS/DTLS network data into a subsequence of
* plaintext application data buffers. This <i>"scattering"</i>
* operation decodes, in a single invocation, a sequence of bytes
* into one or more of a given sequence of buffers. Scattering
@@ -688,55 +688,55 @@
* order it was received. The application must properly synchronize
* multiple calls to this method.
* <P>
- * If this <code>SSLEngine</code> has not yet started its initial
+ * If this {@code SSLEngine} has not yet started its initial
* handshake, this method will automatically start the handshake.
* <P>
- * This method will attempt to consume one complete SSL/TLS network
+ * This method will attempt to consume one complete SSL/TLS/DTLS network
* packet, but will never consume more than the sum of the bytes
- * remaining in the buffers. Each <code>ByteBuffer</code>'s
+ * remaining in the buffers. Each {@code ByteBuffer}'s
* position is updated to reflect the amount of data consumed or
* produced. The limits remain the same.
* <P>
- * The underlying memory used by the <code>src</code> and
- * <code>dsts ByteBuffer</code>s must not be the same.
+ * The underlying memory used by the {@code src} and
+ * {@code dsts ByteBuffer}s must not be the same.
* <P>
* The inbound network buffer may be modified as a result of this
* call: therefore if the network data packet is required for some
* secondary purpose, the data should be duplicated before calling this
* method. Note: the network data will not be useful to a second
* SSLEngine, as each SSLEngine contains unique random state which
- * influences the SSL/TLS messages.
+ * influences the SSL/TLS/DTLS messages.
* <P>
* See the class description for more information on engine closure.
*
* @param src
- * a <code>ByteBuffer</code> containing inbound network data.
+ * a {@code ByteBuffer} containing inbound network data.
* @param dsts
- * an array of <code>ByteBuffer</code>s to hold inbound
+ * an array of {@code ByteBuffer}s to hold inbound
* application data.
* @param offset
* The offset within the buffer array of the first buffer from
* which bytes are to be transferred; it must be non-negative
- * and no larger than <code>dsts.length</code>.
+ * and no larger than {@code dsts.length}.
* @param length
* The maximum number of buffers to be accessed; it must be
* non-negative and no larger than
- * <code>dsts.length</code> - <code>offset</code>.
- * @return an <code>SSLEngineResult</code> describing the result
+ * {@code dsts.length} - {@code offset}.
+ * @return an {@code SSLEngineResult} describing the result
* of this operation.
* @throws SSLException
* A problem was encountered while processing the
- * data that caused the <code>SSLEngine</code> to abort.
+ * data that caused the {@code SSLEngine} to abort.
* See the class description for more information on
* engine closure.
* @throws IndexOutOfBoundsException
- * If the preconditions on the <code>offset</code> and
- * <code>length</code> parameters do not hold.
+ * If the preconditions on the {@code offset} and
+ * {@code length} parameters do not hold.
* @throws ReadOnlyBufferException
- * if any of the <code>dst</code> buffers are read-only.
+ * if any of the {@code dst} buffers are read-only.
* @throws IllegalArgumentException
- * if either <code>src</code> or <code>dsts</code>
- * is null, or if any element in the <code>dsts</code>
+ * if either {@code src} or {@code dsts}
+ * is null, or if any element in the {@code dsts}
* subsequence specified is null.
* @throws IllegalStateException if the client/server mode
* has not yet been set.
@@ -749,19 +749,19 @@
/**
- * Returns a delegated <code>Runnable</code> task for
- * this <code>SSLEngine</code>.
+ * Returns a delegated {@code Runnable} task for
+ * this {@code SSLEngine}.
* <P>
- * <code>SSLEngine</code> operations may require the results of
+ * {@code SSLEngine} operations may require the results of
* operations that block, or may take an extended period of time to
* complete. This method is used to obtain an outstanding {@link
* java.lang.Runnable} operation (task). Each task must be assigned
* a thread (possibly the current) to perform the {@link
* java.lang.Runnable#run() run} operation. Once the
- * <code>run</code> method returns, the <code>Runnable</code> object
+ * {@code run} method returns, the {@code Runnable} object
* is no longer needed and may be discarded.
* <P>
- * Delegated tasks run in the <code>AccessControlContext</code>
+ * Delegated tasks run in the {@code AccessControlContext}
* in place when this object was created.
* <P>
* A call to this method will return each outstanding task
@@ -769,7 +769,7 @@
* <P>
* Multiple delegated tasks can be run in parallel.
*
- * @return a delegated <code>Runnable</code> task, or null
+ * @return a delegated {@code Runnable} task, or null
* if none are available.
*/
public abstract Runnable getDelegatedTask();
@@ -777,7 +777,7 @@
/**
* Signals that no more inbound network data will be sent
- * to this <code>SSLEngine</code>.
+ * to this {@code SSLEngine}.
* <P>
* If the application initiated the closing process by calling
* {@link #closeOutbound()}, under some circumstances it is not
@@ -789,9 +789,9 @@
* <P>
* But if the application did not initiate the closure process, or
* if the circumstances above do not apply, this method should be
- * called whenever the end of the SSL/TLS data stream is reached.
+ * called whenever the end of the SSL/TLS/DTLS data stream is reached.
* This ensures closure of the inbound side, and checks that the
- * peer followed the SSL/TLS close procedure properly, thus
+ * peer followed the SSL/TLS/DTLS close procedure properly, thus
* detecting possible truncation attacks.
* <P>
* This method is idempotent: if the inbound side has already
@@ -801,7 +801,7 @@
* called to flush any remaining handshake data.
*
* @throws SSLException
- * if this engine has not received the proper SSL/TLS close
+ * if this engine has not received the proper SSL/TLS/DTLS close
* notification message from the peer.
*
* @see #isInboundDone()
@@ -814,7 +814,7 @@
* Returns whether {@link #unwrap(ByteBuffer, ByteBuffer)} will
* accept any more inbound data messages.
*
- * @return true if the <code>SSLEngine</code> will not
+ * @return true if the {@code SSLEngine} will not
* consume anymore network data (and by implication,
* will not produce any more application data.)
* @see #closeInbound()
@@ -824,7 +824,7 @@
/**
* Signals that no more outbound application data will be sent
- * on this <code>SSLEngine</code>.
+ * on this {@code SSLEngine}.
* <P>
* This method is idempotent: if the outbound side has already
* been closed, this method does not do anything.
@@ -841,12 +841,12 @@
* Returns whether {@link #wrap(ByteBuffer, ByteBuffer)} will
* produce any more outbound data messages.
* <P>
- * Note that during the closure phase, a <code>SSLEngine</code> may
+ * Note that during the closure phase, a {@code SSLEngine} may
* generate handshake closure data that must be sent to the peer.
- * <code>wrap()</code> must be called to generate this data. When
+ * {@code wrap()} must be called to generate this data. When
* this method returns true, no more outbound data will be created.
*
- * @return true if the <code>SSLEngine</code> will not produce
+ * @return true if the {@code SSLEngine} will not produce
* any more network data
*
* @see #closeOutbound()
@@ -890,10 +890,10 @@
/**
* Sets the cipher suites enabled for use on this engine.
* <P>
- * Each cipher suite in the <code>suites</code> parameter must have
+ * Each cipher suite in the {@code suites} parameter must have
* been listed by getSupportedCipherSuites(), or the method will
* fail. Following a successful call to this method, only suites
- * listed in the <code>suites</code> parameter are enabled for use.
+ * listed in the {@code suites} parameter are enabled for use.
* <P>
* See {@link #getEnabledCipherSuites()} for more information
* on why a specific cipher suite may never be used on a engine.
@@ -910,7 +910,7 @@
/**
* Returns the names of the protocols which could be enabled for use
- * with this <code>SSLEngine</code>.
+ * with this {@code SSLEngine}.
*
* @return an array of protocols supported
*/
@@ -919,7 +919,7 @@
/**
* Returns the names of the protocol versions which are currently
- * enabled for use with this <code>SSLEngine</code>.
+ * enabled for use with this {@code SSLEngine}.
*
* @return an array of protocols
* @see #setEnabledProtocols(String [])
@@ -932,7 +932,7 @@
* <P>
* The protocols must have been listed by getSupportedProtocols()
* as being supported. Following a successful call to this method,
- * only protocols listed in the <code>protocols</code> parameter
+ * only protocols listed in the {@code protocols} parameter
* are enabled for use.
*
* @param protocols Names of all the protocols to enable.
@@ -945,8 +945,8 @@
/**
- * Returns the <code>SSLSession</code> in use in this
- * <code>SSLEngine</code>.
+ * Returns the {@code SSLSession} in use in this
+ * {@code SSLEngine}.
* <P>
* These can be long lived, and frequently correspond to an entire
* login session for some user. The session specifies a particular
@@ -961,22 +961,22 @@
* a session object which reports an invalid cipher suite of
* "SSL_NULL_WITH_NULL_NULL".
*
- * @return the <code>SSLSession</code> for this <code>SSLEngine</code>
+ * @return the {@code SSLSession} for this {@code SSLEngine}
* @see SSLSession
*/
public abstract SSLSession getSession();
/**
- * Returns the {@code SSLSession} being constructed during a SSL/TLS
+ * Returns the {@code SSLSession} being constructed during a SSL/TLS/DTLS
* handshake.
* <p>
- * TLS protocols may negotiate parameters that are needed when using
+ * TLS/DTLS protocols may negotiate parameters that are needed when using
* an instance of this class, but before the {@code SSLSession} has
* been completely initialized and made available via {@code getSession}.
* For example, the list of valid signature algorithms may restrict
* the type of certificates that can used during TrustManager
- * decisions, or the maximum TLS fragment packet sizes can be
+ * decisions, or the maximum TLS/DTLS fragment packet sizes can be
* resized to better support the network environment.
* <p>
* This method provides early access to the {@code SSLSession} being
@@ -1012,26 +1012,26 @@
* Initiates handshaking (initial or renegotiation) on this SSLEngine.
* <P>
* This method is not needed for the initial handshake, as the
- * <code>wrap()</code> and <code>unwrap()</code> methods will
+ * {@code wrap()} and {@code unwrap()} methods will
* implicitly call this method if handshaking has not already begun.
* <P>
* Note that the peer may also request a session renegotiation with
- * this <code>SSLEngine</code> by sending the appropriate
+ * this {@code SSLEngine} by sending the appropriate
* session renegotiate handshake message.
* <P>
* Unlike the {@link SSLSocket#startHandshake()
* SSLSocket#startHandshake()} method, this method does not block
* until handshaking is completed.
* <P>
- * To force a complete SSL/TLS session renegotiation, the current
+ * To force a complete SSL/TLS/DTLS session renegotiation, the current
* session should be invalidated prior to calling this method.
* <P>
* Some protocols may not support multiple handshakes on an existing
- * engine and may throw an <code>SSLException</code>.
+ * engine and may throw an {@code SSLException}.
*
* @throws SSLException
* if a problem was encountered while signaling the
- * <code>SSLEngine</code> to begin a new handshake.
+ * {@code SSLEngine} to begin a new handshake.
* See the class description for more information on
* engine closure.
* @throws IllegalStateException if the client/server mode
@@ -1042,9 +1042,9 @@
/**
- * Returns the current handshake status for this <code>SSLEngine</code>.
+ * Returns the current handshake status for this {@code SSLEngine}.
*
- * @return the current <code>SSLEngineResult.HandshakeStatus</code>.
+ * @return the current {@code SSLEngineResult.HandshakeStatus}.
*/
public abstract SSLEngineResult.HandshakeStatus getHandshakeStatus();
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngineResult.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngineResult.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,13 +27,13 @@
/**
* An encapsulation of the result state produced by
- * <code>SSLEngine</code> I/O calls.
+ * {@code SSLEngine} I/O calls.
*
- * <p> A <code>SSLEngine</code> provides a means for establishing
- * secure communication sessions between two peers. <code>SSLEngine</code>
+ * <p> A {@code SSLEngine} provides a means for establishing
+ * secure communication sessions between two peers. {@code SSLEngine}
* operations typically consume bytes from an input buffer and produce
* bytes in an output buffer. This class provides operational result
- * values describing the state of the <code>SSLEngine</code>, including
+ * values describing the state of the {@code SSLEngine}, including
* indications of what operations are needed to finish an
* ongoing handshake. Lastly, it reports the number of bytes consumed
* and produced as a result of this operation.
@@ -49,12 +49,12 @@
public class SSLEngineResult {
/**
- * An <code>SSLEngineResult</code> enum describing the overall result
- * of the <code>SSLEngine</code> operation.
+ * An {@code SSLEngineResult} enum describing the overall result
+ * of the {@code SSLEngine} operation.
*
- * The <code>Status</code> value does not reflect the
- * state of a <code>SSLEngine</code> handshake currently
- * in progress. The <code>SSLEngineResult's HandshakeStatus</code>
+ * The {@code Status} value does not reflect the
+ * state of a {@code SSLEngine} handshake currently
+ * in progress. The {@code SSLEngineResult's HandshakeStatus}
* should be consulted for that information.
*
* @author Brad R. Wetmore
@@ -63,7 +63,7 @@
public static enum Status {
/**
- * The <code>SSLEngine</code> was not able to unwrap the
+ * The {@code SSLEngine} was not able to unwrap the
* incoming data because there were not enough source bytes
* available to make a complete packet.
*
@@ -73,7 +73,7 @@
BUFFER_UNDERFLOW,
/**
- * The <code>SSLEngine</code> was not able to process the
+ * The {@code SSLEngine} was not able to process the
* operation because there are not enough bytes available in the
* destination buffer to hold the result.
* <P>
@@ -85,22 +85,22 @@
BUFFER_OVERFLOW,
/**
- * The <code>SSLEngine</code> completed the operation, and
+ * The {@code SSLEngine} completed the operation, and
* is available to process similar calls.
*/
OK,
/**
* The operation just closed this side of the
- * <code>SSLEngine</code>, or the operation
+ * {@code SSLEngine}, or the operation
* could not be completed because it was already closed.
*/
CLOSED;
}
/**
- * An <code>SSLEngineResult</code> enum describing the current
- * handshaking state of this <code>SSLEngine</code>.
+ * An {@code SSLEngineResult} enum describing the current
+ * handshaking state of this {@code SSLEngine}.
*
* @author Brad R. Wetmore
* @since 1.5
@@ -108,17 +108,17 @@
public static enum HandshakeStatus {
/**
- * The <code>SSLEngine</code> is not currently handshaking.
+ * The {@code SSLEngine} is not currently handshaking.
*/
NOT_HANDSHAKING,
/**
- * The <code>SSLEngine</code> has just finished handshaking.
+ * The {@code SSLEngine} has just finished handshaking.
* <P>
* This value is only generated by a call to
- * <code>SSLEngine.wrap()/unwrap()</code> when that call
+ * {@code SSLEngine.wrap()/unwrap()} when that call
* finishes a handshake. It is never generated by
- * <code>SSLEngine.getHandshakeStatus()</code>.
+ * {@code SSLEngine.getHandshakeStatus()}.
*
* @see SSLEngine#wrap(ByteBuffer, ByteBuffer)
* @see SSLEngine#unwrap(ByteBuffer, ByteBuffer)
@@ -127,7 +127,7 @@
FINISHED,
/**
- * The <code>SSLEngine</code> needs the results of one (or more)
+ * The {@code SSLEngine} needs the results of one (or more)
* delegated tasks before handshaking can continue.
*
* @see SSLEngine#getDelegatedTask()
@@ -135,8 +135,8 @@
NEED_TASK,
/**
- * The <code>SSLEngine</code> must send data to the remote side
- * before handshaking can continue, so <code>SSLEngine.wrap()</code>
+ * The {@code SSLEngine} must send data to the remote side
+ * before handshaking can continue, so {@code SSLEngine.wrap()}
* should be called.
*
* @see SSLEngine#wrap(ByteBuffer, ByteBuffer)
@@ -144,10 +144,22 @@
NEED_WRAP,
/**
- * The <code>SSLEngine</code> needs to receive data from the
+ * The {@code SSLEngine} needs to receive data from the
* remote side before handshaking can continue.
*/
- NEED_UNWRAP;
+ NEED_UNWRAP,
+
+ /**
+ * The {@code SSLEngine} needs to unwrap before handshaking can
+ * can continue.
+ * <P>
+ * This value is used to indicate that not-yet-interpreted data
+ * has been previously received from the remote side, and does
+ * not need to be received again.
+ *
+ * @since 1.9
+ */
+ NEED_UNWRAP_AGAIN;
}
@@ -155,6 +167,7 @@
private final HandshakeStatus handshakeStatus;
private final int bytesConsumed;
private final int bytesProduced;
+ private final long sequenceNumber;
/**
* Initializes a new instance of this class.
@@ -172,12 +185,44 @@
* the number of bytes placed into the destination ByteBuffer
*
* @throws IllegalArgumentException
- * if the <code>status</code> or <code>handshakeStatus</code>
- * arguments are null, or if <code>bytesConsumed</code> or
- * <code>bytesProduced</code> is negative.
+ * if the {@code status} or {@code handshakeStatus}
+ * arguments are null, or if {@code bytesConsumed} or
+ * {@code bytesProduced} is negative.
*/
public SSLEngineResult(Status status, HandshakeStatus handshakeStatus,
int bytesConsumed, int bytesProduced) {
+ this(status, handshakeStatus, bytesConsumed, bytesProduced, -1);
+ }
+
+ /**
+ * Initializes a new instance of this class.
+ *
+ * @param status
+ * the return value of the operation.
+ *
+ * @param handshakeStatus
+ * the current handshaking status.
+ *
+ * @param bytesConsumed
+ * the number of bytes consumed from the source ByteBuffer
+ *
+ * @param bytesProduced
+ * the number of bytes placed into the destination ByteBuffer
+ *
+ * @param sequenceNumber
+ * the sequence number (unsigned long) of the produced or
+ * consumed SSL/TLS/DTLS record, or ${@code -1L} if no record
+ * produced or consumed
+ *
+ * @throws IllegalArgumentException
+ * if the {@code status} or {@code handshakeStatus}
+ * arguments are null, or if {@code bytesConsumed} or
+ * {@code bytesProduced} is negative
+ *
+ * @since 1.9
+ */
+ public SSLEngineResult(Status status, HandshakeStatus handshakeStatus,
+ int bytesConsumed, int bytesProduced, long sequenceNumber) {
if ((status == null) || (handshakeStatus == null) ||
(bytesConsumed < 0) || (bytesProduced < 0)) {
@@ -188,10 +233,11 @@
this.handshakeStatus = handshakeStatus;
this.bytesConsumed = bytesConsumed;
this.bytesProduced = bytesProduced;
+ this.sequenceNumber = sequenceNumber;
}
/**
- * Gets the return value of this <code>SSLEngine</code> operation.
+ * Gets the return value of this {@code SSLEngine} operation.
*
* @return the return value
*/
@@ -200,7 +246,7 @@
}
/**
- * Gets the handshake status of this <code>SSLEngine</code>
+ * Gets the handshake status of this {@code SSLEngine}
* operation.
*
* @return the handshake status
@@ -228,6 +274,41 @@
}
/**
+ * Returns the sequence number of the produced or consumed SSL/TLS/DTLS
+ * record (optional operation).
+ *
+ * @apiNote Note that sequence number is an unsigned long and cannot
+ * exceed {@code -1L}. It is desired to use the unsigned
+ * long comparing mode for comparison of unsigned long values
+ * (see also {@link java.lang.Long#compareUnsigned(long, long)
+ * Long.compareUnsigned()}).
+ * <P>
+ * For DTLS protocols, the first 16 bits of the sequence
+ * number is a counter value (epoch) that is incremented on
+ * every cipher state change. The remaining 48 bits on the
+ * right side of the sequence number represents the sequence
+ * of the record, which is maintained separately for each epoch.
+ *
+ * @implNote It is recommended that providers should never allow the
+ * sequence number incremented to {@code -1L}. If the sequence
+ * number is close to wrapping, renegotiate should be requested,
+ * otherwise the connection should be closed immediately.
+ * This should be carried on automatically by the underlying
+ * implementation.
+ *
+ * @return the sequence number of the produced or consumed SSL/TLS/DTLS
+ * record; or ${@code -1L} if no record is produced or consumed,
+ * or this operation is not supported by the underlying provider
+ *
+ * @see java.lang.Long#compareUnsigned(long, long)
+ *
+ * @since 1.9
+ */
+ final public long sequenceNumber() {
+ return sequenceNumber;
+ }
+
+ /**
* Returns a String representation of this object.
*/
@Override
@@ -235,6 +316,8 @@
return ("Status = " + status +
" HandshakeStatus = " + handshakeStatus +
"\nbytesConsumed = " + bytesConsumed +
- " bytesProduced = " + bytesProduced);
+ " bytesProduced = " + bytesProduced +
+ (sequenceNumber == -1 ? "" :
+ " sequenceNumber = " + Long.toUnsignedString(sequenceNumber)));
}
}
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLParameters.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLParameters.java Thu Jun 04 18:49:37 2015 -0700
@@ -35,22 +35,22 @@
import java.util.LinkedHashMap;
/**
- * Encapsulates parameters for an SSL/TLS connection. The parameters
- * are the list of ciphersuites to be accepted in an SSL/TLS handshake,
+ * Encapsulates parameters for an SSL/TLS/DTLS connection. The parameters
+ * are the list of ciphersuites to be accepted in an SSL/TLS/DTLS handshake,
* the list of protocols to be allowed, the endpoint identification
- * algorithm during SSL/TLS handshaking, the Server Name Indication (SNI),
- * the algorithm constraints and whether SSL/TLS servers should request
- * or require client authentication, etc.
+ * algorithm during SSL/TLS/DTLS handshaking, the Server Name Indication (SNI),
+ * the maximum network packet size, the algorithm constraints and whether
+ * SSL/TLS/DTLS servers should request or require client authentication, etc.
* <p>
* SSLParameters can be created via the constructors in this class.
- * Objects can also be obtained using the <code>getSSLParameters()</code>
+ * Objects can also be obtained using the {@code getSSLParameters()}
* methods in
* {@link SSLSocket#getSSLParameters SSLSocket} and
* {@link SSLServerSocket#getSSLParameters SSLServerSocket} and
* {@link SSLEngine#getSSLParameters SSLEngine} or the
* {@link SSLContext#getDefaultSSLParameters getDefaultSSLParameters()} and
* {@link SSLContext#getSupportedSSLParameters getSupportedSSLParameters()}
- * methods in <code>SSLContext</code>.
+ * methods in {@code SSLContext}.
* <p>
* SSLParameters can be applied to a connection via the methods
* {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and
@@ -74,14 +74,18 @@
private Map<Integer, SNIServerName> sniNames = null;
private Map<Integer, SNIMatcher> sniMatchers = null;
private boolean preferLocalCipherSuites;
+ private boolean enableRetransmissions = true;
+ private int maximumPacketSize = 0;
/**
* Constructs SSLParameters.
* <p>
* The values of cipherSuites, protocols, cryptographic algorithm
* constraints, endpoint identification algorithm, server names and
- * server name matchers are set to <code>null</code>, useCipherSuitesOrder,
- * wantClientAuth and needClientAuth are set to <code>false</code>.
+ * server name matchers are set to {@code null}; useCipherSuitesOrder,
+ * wantClientAuth and needClientAuth are set to {@code false};
+ * enableRetransmissions is set to {@code true}; maximum network packet
+ * size is set to {@code 0}.
*/
public SSLParameters() {
// empty
@@ -92,7 +96,7 @@
* <p>
* Calling this constructor is equivalent to calling the no-args
* constructor followed by
- * <code>setCipherSuites(cipherSuites);</code>.
+ * {@code setCipherSuites(cipherSuites);}.
*
* @param cipherSuites the array of ciphersuites (or null)
*/
@@ -106,7 +110,7 @@
* <p>
* Calling this constructor is equivalent to calling the no-args
* constructor followed by
- * <code>setCipherSuites(cipherSuites); setProtocols(protocols);</code>.
+ * {@code setCipherSuites(cipherSuites); setProtocols(protocols);}.
*
* @param cipherSuites the array of ciphersuites (or null)
* @param protocols the array of protocols (or null)
@@ -171,7 +175,7 @@
/**
* Sets whether client authentication should be requested. Calling
- * this method clears the <code>needClientAuth</code> flag.
+ * this method clears the {@code needClientAuth} flag.
*
* @param wantClientAuth whether client authentication should be requested
*/
@@ -191,7 +195,7 @@
/**
* Sets whether client authentication should be required. Calling
- * this method clears the <code>wantClientAuth</code> flag.
+ * this method clears the {@code wantClientAuth} flag.
*
* @param needClientAuth whether client authentication should be required
*/
@@ -218,9 +222,9 @@
* Sets the cryptographic algorithm constraints, which will be used
* in addition to any configured by the runtime environment.
* <p>
- * If the <code>constraints</code> parameter is non-null, every
+ * If the {@code constraints} parameter is non-null, every
* cryptographic algorithm, key and algorithm parameters used in the
- * SSL/TLS handshake must be permitted by the constraints.
+ * SSL/TLS/DTLS handshake must be permitted by the constraints.
*
* @param constraints the algorithm constraints (or null)
*
@@ -249,9 +253,9 @@
/**
* Sets the endpoint identification algorithm.
* <p>
- * If the <code>algorithm</code> parameter is non-null or non-empty, the
+ * If the {@code algorithm} parameter is non-null or non-empty, the
* endpoint identification/verification procedures must be handled during
- * SSL/TLS handshaking. This is to prevent man-in-the-middle attacks.
+ * SSL/TLS/DTLS handshaking. This is to prevent man-in-the-middle attacks.
*
* @param algorithm The standard string name of the endpoint
* identification algorithm (or null). See Appendix A in the <a href=
@@ -317,7 +321,7 @@
* This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s
* operating in client mode.
* <P>
- * For SSL/TLS connections, the underlying SSL/TLS provider
+ * For SSL/TLS/DTLS connections, the underlying SSL/TLS/DTLS provider
* may specify a default value for a certain server name type. In
* client mode, it is recommended that, by default, providers should
* include the server name indication whenever the server can be located
@@ -440,7 +444,7 @@
*
* @param honorOrder whether local cipher suites order in
* {@code #getCipherSuites} should be honored during
- * SSL/TLS handshaking.
+ * SSL/TLS/DTLS handshaking.
*
* @see #getUseCipherSuitesOrder()
*
@@ -454,7 +458,7 @@
* Returns whether the local cipher suites preference should be honored.
*
* @return whether local cipher suites order in {@code #getCipherSuites}
- * should be honored during SSL/TLS handshaking.
+ * should be honored during SSL/TLS/DTLS handshaking.
*
* @see #setUseCipherSuitesOrder(boolean)
*
@@ -463,5 +467,107 @@
public final boolean getUseCipherSuitesOrder() {
return preferLocalCipherSuites;
}
+
+ /**
+ * Sets whether DTLS handshake retransmissions should be enabled.
+ *
+ * This method only applies to DTLS.
+ *
+ * @param enableRetransmissions
+ * {@code true} indicates that DTLS handshake retransmissions
+ * should be enabled; {@code false} indicates that DTLS handshake
+ * retransmissions should be disabled
+ *
+ * @see #getEnableRetransmissions()
+ *
+ * @since 1.9
+ */
+ public void setEnableRetransmissions(boolean enableRetransmissions) {
+ this.enableRetransmissions = enableRetransmissions;
+ }
+
+ /**
+ * Returns whether DTLS handshake retransmissions should be enabled.
+ *
+ * This method only applies to DTLS.
+ *
+ * @return true, if DTLS handshake retransmissions should be enabled
+ *
+ * @see #setEnableRetransmissions(boolean)
+ *
+ * @since 1.9
+ */
+ public boolean getEnableRetransmissions() {
+ return enableRetransmissions;
+ }
+
+ /**
+ * Sets the maximum expected network packet size in bytes for
+ * SSL/TLS/DTLS records.
+ *
+ * @apiNote It is recommended that if possible, the maximum packet size
+ * should not be less than 256 bytes so that small handshake
+ * messages, such as HelloVerifyRequests, are not fragmented.
+ *
+ * @implNote If the maximum packet size is too small to hold a minimal
+ * record, an implementation may attempt to generate as minimal
+ * records as possible. However, this may cause a generated
+ * packet to be larger than the maximum packet size.
+ *
+ * @param maximumPacketSize
+ * the maximum expected network packet size in bytes, or
+ * {@code 0} to use the implicit size that is automatically
+ * specified by the underlying implementation.
+ * @throws IllegalArgumentException
+ * if {@code maximumPacketSize} is negative.
+ *
+ * @see #getMaximumPacketSize()
+ *
+ * @since 1.9
+ */
+ public void setMaximumPacketSize(int maximumPacketSize) {
+ if (maximumPacketSize < 0) {
+ throw new IllegalArgumentException(
+ "The maximum packet size cannot be negative");
+ }
+
+ this.maximumPacketSize = maximumPacketSize;
+ }
+
+ /**
+ * Returns the maximum expected network packet size in bytes for
+ * SSL/TLS/DTLS records.
+ *
+ * @apiNote The implicit size may not be a fixed value, especially
+ * for a DTLS protocols implementation.
+ *
+ * @implNote For SSL/TLS/DTLS connections, the underlying provider
+ * should calculate and specify the implicit value of the
+ * maximum expected network packet size if it is not
+ * configured explicitly. For any connection populated
+ * object, this method should never return {@code 0} so
+ * that applications can retrieve the actual implicit size
+ * of the underlying implementation.
+ * <P>
+ * An implementation should attempt to comply with the maximum
+ * packet size configuration. However, if the maximum packet
+ * size is too small to hold a minimal record, an implementation
+ * may try to generate as minimal records as possible. This
+ * may cause a generated packet to be larger than the maximum
+ * packet size.
+ *
+ * @return the maximum expected network packet size, or {@code 0} if
+ * use the implicit size that is automatically specified by
+ * the underlying implementation and this object has not been
+ * populated by any connection.
+ *
+ * @see #setMaximumPacketSize(int)
+ *
+ * @since 1.9
+ */
+ public int getMaximumPacketSize() {
+ return maximumPacketSize;
+ }
+
}
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLSession.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLSession.java Thu Jun 04 18:49:37 2015 -0700
@@ -35,7 +35,7 @@
* also be replaced by a different session. Sessions are created, or
* rejoined, as part of the SSL handshaking protocol. Sessions may be
* invalidated due to policies affecting security or resource usage,
- * or by an application explicitly calling <code>invalidate</code>.
+ * or by an application explicitly calling {@code invalidate}.
* Session management policies are typically used to tune performance.
*
* <P> In addition to the standard session attributes, SSL sessions expose
@@ -82,8 +82,8 @@
* security manager installed, the caller may require
* permission to access it or a security exception may be thrown.
* In a Java environment, the security manager's
- * <code>checkPermission</code> method is called with a
- * <code>SSLPermission("getSSLSessionContext")</code> permission.
+ * {@code checkPermission} method is called with a
+ * {@code SSLPermission("getSSLSessionContext")} permission.
*
* @throws SecurityException if the calling thread does not have
* permission to get SSL session context.
@@ -148,14 +148,14 @@
/**
*
- * Binds the specified <code>value</code> object into the
+ * Binds the specified {@code value} object into the
* session's application layer data
- * with the given <code>name</code>.
+ * with the given {@code name}.
* <P>
- * Any existing binding using the same <code>name</code> is
- * replaced. If the new (or existing) <code>value</code> implements the
- * <code>SSLSessionBindingListener</code> interface, the object
- * represented by <code>value</code> is notified appropriately.
+ * Any existing binding using the same {@code name} is
+ * replaced. If the new (or existing) {@code value} implements the
+ * {@code SSLSessionBindingListener} interface, the object
+ * represented by {@code value} is notified appropriately.
* <p>
* For security reasons, the same named values may not be
* visible across different access control contexts.
@@ -187,7 +187,7 @@
* Removes the object bound to the given name in the session's
* application layer data. Does nothing if there is no object
* bound to the given name. If the bound existing object
- * implements the <code>SessionBindingListener</code> interface,
+ * implements the {@code SessionBindingListener} interface,
* it is notified appropriately.
* <p>
* For security reasons, the same named values may not be
@@ -349,7 +349,7 @@
* by this method.
* <P>
* This value is not authenticated and should not be relied upon.
- * It is mainly used as a hint for <code>SSLSession</code> caching
+ * It is mainly used as a hint for {@code SSLSession} caching
* strategies.
*
* @return the host name of the peer host, or null if no information
@@ -364,7 +364,7 @@
* the client, it is the server's port number.
* <P>
* This value is not authenticated and should not be relied upon.
- * It is mainly used as a hint for <code>SSLSession</code> caching
+ * It is mainly used as a hint for {@code SSLSession} caching
* strategies.
*
* @return the port number of the peer host, or -1 if no information
@@ -375,14 +375,14 @@
public int getPeerPort();
/**
- * Gets the current size of the largest SSL/TLS packet that is expected
- * when using this session.
+ * Gets the current size of the largest SSL/TLS/DTLS packet that is
+ * expected when using this session.
* <P>
- * A <code>SSLEngine</code> using this session may generate SSL/TLS
+ * An {@code SSLEngine} using this session may generate SSL/TLS/DTLS
* packets of any size up to and including the value returned by this
- * method. All <code>SSLEngine</code> network buffers should be sized
+ * method. All {@code SSLEngine} network buffers should be sized
* at least this large to avoid insufficient space problems when
- * performing <code>wrap</code> and <code>unwrap</code> calls.
+ * performing {@code wrap} and {@code unwrap} calls.
*
* @return the current maximum expected network packet size
*
@@ -398,7 +398,7 @@
* Gets the current size of the largest application data that is
* expected when using this session.
* <P>
- * <code>SSLEngine</code> application data buffers must be large
+ * {@code SSLEngine} application data buffers must be large
* enough to hold the application data from any inbound network
* application data packet received. Typically, outbound
* application data buffers can be of any size.
--- a/jdk/src/java.base/share/classes/javax/net/ssl/X509ExtendedTrustManager.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/X509ExtendedTrustManager.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,16 +32,17 @@
import java.security.cert.CertificateException;
/**
- * Extensions to the <code>X509TrustManager</code> interface to support
- * SSL/TLS connection sensitive trust management.
+ * Extensions to the {@code X509TrustManager} interface to support
+ * SSL/TLS/DTLS connection sensitive trust management.
* <p>
* To prevent man-in-the-middle attacks, hostname checks can be done
* to verify that the hostname in an end-entity certificate matches the
- * targeted hostname. TLS does not require such checks, but some protocols
- * over TLS (such as HTTPS) do. In earlier versions of the JDK, the
- * certificate chain checks were done at the SSL/TLS layer, and the hostname
- * verification checks were done at the layer over TLS. This class allows
- * for the checking to be done during a single call to this class.
+ * targeted hostname. TLS/DTLS does not require such checks, but some
+ * protocols over TLS/DTLS (such as HTTPS) do. In earlier versions of the
+ * JDK, the certificate chain checks were done at the SSL/TLS/DTLS layer,
+ * and the hostname verification checks were done at the layer over TLS/DTLS.
+ * This class allows for the checking to be done during a single call to
+ * this class.
* <p>
* RFC 2830 defines the server identification specification for the "LDAPS"
* algorithm. RFC 2818 defines both the server identification and the
@@ -62,17 +63,17 @@
* used. For instance, if RSAPublicKey is used, the authType
* should be "RSA". Checking is case-sensitive.
* <p>
- * If the <code>socket</code> parameter is an instance of
+ * If the {@code socket} parameter is an instance of
* {@link javax.net.ssl.SSLSocket}, and the endpoint identification
- * algorithm of the <code>SSLParameters</code> is non-empty, to prevent
- * man-in-the-middle attacks, the address that the <code>socket</code>
+ * algorithm of the {@code SSLParameters} is non-empty, to prevent
+ * man-in-the-middle attacks, the address that the {@code socket}
* connected to should be checked against the peer's identity presented
* in the end-entity X509 certificate, as specified in the endpoint
* identification algorithm.
* <p>
- * If the <code>socket</code> parameter is an instance of
+ * If the {@code socket} parameter is an instance of
* {@link javax.net.ssl.SSLSocket}, and the algorithm constraints of the
- * <code>SSLParameters</code> is non-null, for every certificate in the
+ * {@code SSLParameters} is non-null, for every certificate in the
* certification path, fields such as subject public key, the signature
* algorithm, key usage, extended key usage, etc. need to conform to the
* algorithm constraints in place on this socket.
@@ -83,8 +84,8 @@
* can be null, which indicates that implementations need not check
* the ssl parameters
* @throws IllegalArgumentException if null or zero-length array is passed
- * in for the <code>chain</code> parameter or if null or zero-length
- * string is passed in for the <code>authType</code> parameter
+ * in for the {@code chain} parameter or if null or zero-length
+ * string is passed in for the {@code authType} parameter
* @throws CertificateException if the certificate chain is not trusted
* by this TrustManager
*
@@ -110,17 +111,17 @@
* used for the key exchange, and RSA when the key from the server
* certificate is used. Checking is case-sensitive.
* <p>
- * If the <code>socket</code> parameter is an instance of
+ * If the {@code socket} parameter is an instance of
* {@link javax.net.ssl.SSLSocket}, and the endpoint identification
- * algorithm of the <code>SSLParameters</code> is non-empty, to prevent
- * man-in-the-middle attacks, the address that the <code>socket</code>
+ * algorithm of the {@code SSLParameters} is non-empty, to prevent
+ * man-in-the-middle attacks, the address that the {@code socket}
* connected to should be checked against the peer's identity presented
* in the end-entity X509 certificate, as specified in the endpoint
* identification algorithm.
* <p>
- * If the <code>socket</code> parameter is an instance of
+ * If the {@code socket} parameter is an instance of
* {@link javax.net.ssl.SSLSocket}, and the algorithm constraints of the
- * <code>SSLParameters</code> is non-null, for every certificate in the
+ * {@code SSLParameters} is non-null, for every certificate in the
* certification path, fields such as subject public key, the signature
* algorithm, key usage, extended key usage, etc. need to conform to the
* algorithm constraints in place on this socket.
@@ -131,8 +132,8 @@
* can be null, which indicates that implementations need not check
* the ssl parameters
* @throws IllegalArgumentException if null or zero-length array is passed
- * in for the <code>chain</code> parameter or if null or zero-length
- * string is passed in for the <code>authType</code> parameter
+ * in for the {@code chain} parameter or if null or zero-length
+ * string is passed in for the {@code authType} parameter
* @throws CertificateException if the certificate chain is not trusted
* by this TrustManager
*
@@ -153,15 +154,15 @@
* used. For instance, if RSAPublicKey is used, the authType
* should be "RSA". Checking is case-sensitive.
* <p>
- * If the <code>engine</code> parameter is available, and the endpoint
- * identification algorithm of the <code>SSLParameters</code> is
+ * If the {@code engine} parameter is available, and the endpoint
+ * identification algorithm of the {@code SSLParameters} is
* non-empty, to prevent man-in-the-middle attacks, the address that
- * the <code>engine</code> connected to should be checked against
+ * the {@code engine} connected to should be checked against
* the peer's identity presented in the end-entity X509 certificate,
* as specified in the endpoint identification algorithm.
* <p>
- * If the <code>engine</code> parameter is available, and the algorithm
- * constraints of the <code>SSLParameters</code> is non-null, for every
+ * If the {@code engine} parameter is available, and the algorithm
+ * constraints of the {@code SSLParameters} is non-null, for every
* certificate in the certification path, fields such as subject public
* key, the signature algorithm, key usage, extended key usage, etc.
* need to conform to the algorithm constraints in place on this engine.
@@ -172,8 +173,8 @@
* can be null, which indicates that implementations need not check
* the ssl parameters
* @throws IllegalArgumentException if null or zero-length array is passed
- * in for the <code>chain</code> parameter or if null or zero-length
- * string is passed in for the <code>authType</code> parameter
+ * in for the {@code chain} parameter or if null or zero-length
+ * string is passed in for the {@code authType} parameter
* @throws CertificateException if the certificate chain is not trusted
* by this TrustManager
*
@@ -199,15 +200,15 @@
* used for the key exchange, and RSA when the key from the server
* certificate is used. Checking is case-sensitive.
* <p>
- * If the <code>engine</code> parameter is available, and the endpoint
- * identification algorithm of the <code>SSLParameters</code> is
+ * If the {@code engine} parameter is available, and the endpoint
+ * identification algorithm of the {@code SSLParameters} is
* non-empty, to prevent man-in-the-middle attacks, the address that
- * the <code>engine</code> connected to should be checked against
+ * the {@code engine} connected to should be checked against
* the peer's identity presented in the end-entity X509 certificate,
* as specified in the endpoint identification algorithm.
* <p>
- * If the <code>engine</code> parameter is available, and the algorithm
- * constraints of the <code>SSLParameters</code> is non-null, for every
+ * If the {@code engine} parameter is available, and the algorithm
+ * constraints of the {@code SSLParameters} is non-null, for every
* certificate in the certification path, fields such as subject public
* key, the signature algorithm, key usage, extended key usage, etc.
* need to conform to the algorithm constraints in place on this engine.
@@ -218,8 +219,8 @@
* can be null, which indicates that implementations need not check
* the ssl parameters
* @throws IllegalArgumentException if null or zero-length array is passed
- * in for the <code>chain</code> parameter or if null or zero-length
- * string is passed in for the <code>authType</code> parameter
+ * in for the {@code chain} parameter or if null or zero-length
+ * string is passed in for the {@code authType} parameter
* @throws CertificateException if the certificate chain is not trusted
* by this TrustManager
*
--- a/jdk/src/java.base/share/classes/sun/invoke/anon/ConstantPoolPatch.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/invoke/anon/ConstantPoolPatch.java Thu Jun 04 18:49:37 2015 -0700
@@ -417,7 +417,7 @@
| CONSTANT_InterfaceMethodref;
private static final Map<Class<?>, Byte> CONSTANT_VALUE_CLASS_TAG
- = new IdentityHashMap<Class<?>, Byte>();
+ = new IdentityHashMap<Class<?>, Byte>(6);
private static final Class<?>[] CONSTANT_VALUE_CLASS = new Class<?>[16];
static {
Object[][] values = {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/misc/JavaBeansAccess.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.misc;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+public interface JavaBeansAccess {
+ /**
+ * Returns the getter method for a property of the given name
+ * @param clazz The JavaBeans class
+ * @param property The property name
+ * @return The resolved property getter method
+ * @throws Exception
+ */
+ Method getReadMethod(Class<?> clazz, String property) throws Exception;
+
+ /**
+ * Return the <b>value</b> attribute of the associated
+ * <code>@ConstructorProperties</code> annotation if that is present.
+ * @param ctr The constructor to extract the annotation value from
+ * @return The {@code value} attribute of the <code>@ConstructorProperties</code>
+ * annotation or {@code null} if the constructor is not annotated by
+ * this annotation or the annotation is not accessible.
+ */
+ String[] getConstructorPropertiesValue(Constructor<?> ctr);
+}
--- a/jdk/src/java.base/share/classes/sun/misc/JavaBeansIntrospectorAccess.java Thu Jun 04 09:31:49 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.misc;
-
-import java.lang.reflect.Method;
-
-public interface JavaBeansIntrospectorAccess {
- Method getReadMethod(Class<?> clazz, String property) throws Exception;
-}
--- a/jdk/src/java.base/share/classes/sun/misc/SharedSecrets.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/misc/SharedSecrets.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -56,7 +56,7 @@
private static JavaUtilZipFileAccess javaUtilZipFileAccess;
private static JavaAWTAccess javaAWTAccess;
private static JavaAWTFontAccess javaAWTFontAccess;
- private static JavaBeansIntrospectorAccess javaBeansIntrospectorAccess;
+ private static JavaBeansAccess javaBeansAccess;
public static JavaUtilJarAccess javaUtilJarAccess() {
if (javaUtilJarAccess == null) {
@@ -194,11 +194,11 @@
return javaAWTFontAccess;
}
- public static JavaBeansIntrospectorAccess getJavaBeansIntrospectorAccess() {
- return javaBeansIntrospectorAccess;
+ public static JavaBeansAccess getJavaBeansAccess() {
+ return javaBeansAccess;
}
- public static void setJavaBeansIntrospectorAccess(JavaBeansIntrospectorAccess access) {
- javaBeansIntrospectorAccess = access;
+ public static void setJavaBeansAccess(JavaBeansAccess access) {
+ javaBeansAccess = access;
}
}
--- a/jdk/src/java.base/share/classes/sun/nio/cs/AbstractCharsetProvider.java Thu Jun 04 09:31:49 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,206 +0,0 @@
-/*
- * Copyright (c) 2000, 2011, 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.nio.cs;
-
-import java.lang.ref.SoftReference;
-import java.nio.charset.Charset;
-import java.nio.charset.spi.CharsetProvider;
-import java.util.ArrayList;
-import java.util.TreeMap;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Map;
-import sun.misc.ASCIICaseInsensitiveComparator;
-
-
-/**
- * Abstract base class for charset providers.
- *
- * @author Mark Reinhold
- */
-
-public class AbstractCharsetProvider
- extends CharsetProvider
-{
-
- /* Maps canonical names to class names
- */
- private Map<String,String> classMap
- = new TreeMap<>(ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER);
-
- /* Maps alias names to canonical names
- */
- private Map<String,String> aliasMap
- = new TreeMap<>(ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER);
-
- /* Maps canonical names to alias-name arrays
- */
- private Map<String,String[]> aliasNameMap
- = new TreeMap<>(ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER);
-
- /* Maps canonical names to soft references that hold cached instances
- */
- private Map<String,SoftReference<Charset>> cache
- = new TreeMap<>(ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER);
-
- private String packagePrefix;
-
- protected AbstractCharsetProvider() {
- packagePrefix = "sun.nio.cs";
- }
-
- protected AbstractCharsetProvider(String pkgPrefixName) {
- packagePrefix = pkgPrefixName;
- }
-
- /* Add an entry to the given map, but only if no mapping yet exists
- * for the given name.
- */
- private static <K,V> void put(Map<K,V> m, K name, V value) {
- if (!m.containsKey(name))
- m.put(name, value);
- }
-
- private static <K,V> void remove(Map<K,V> m, K name) {
- V x = m.remove(name);
- assert (x != null);
- }
-
- /* Declare support for the given charset
- */
- protected void charset(String name, String className, String[] aliases) {
- synchronized (this) {
- put(classMap, name, className);
- for (int i = 0; i < aliases.length; i++)
- put(aliasMap, aliases[i], name);
- put(aliasNameMap, name, aliases);
- cache.clear();
- }
- }
-
- protected void deleteCharset(String name, String[] aliases) {
- synchronized (this) {
- remove(classMap, name);
- for (int i = 0; i < aliases.length; i++)
- remove(aliasMap, aliases[i]);
- remove(aliasNameMap, name);
- cache.clear();
- }
- }
-
- protected boolean hasCharset(String name) {
- synchronized (this) {
- return classMap.containsKey(name);
- }
- }
-
- /* Late initialization hook, needed by some providers
- */
- protected void init() { }
-
- private String canonicalize(String charsetName) {
- String acn = aliasMap.get(charsetName);
- return (acn != null) ? acn : charsetName;
- }
-
- private Charset lookup(String csn) {
-
- // Check cache first
- SoftReference<Charset> sr = cache.get(csn);
- if (sr != null) {
- Charset cs = sr.get();
- if (cs != null)
- return cs;
- }
-
- // Do we even support this charset?
- String cln = classMap.get(csn);
-
- if (cln == null)
- return null;
-
- // Instantiate the charset and cache it
- try {
-
- Class<?> c = Class.forName(packagePrefix + "." + cln,
- true,
- this.getClass().getClassLoader());
-
- Charset cs = (Charset)c.newInstance();
- cache.put(csn, new SoftReference<Charset>(cs));
- return cs;
- } catch (ClassNotFoundException x) {
- return null;
- } catch (IllegalAccessException x) {
- return null;
- } catch (InstantiationException x) {
- return null;
- }
- }
-
- public final Charset charsetForName(String charsetName) {
- synchronized (this) {
- init();
- return lookup(canonicalize(charsetName));
- }
- }
-
- public final Iterator<Charset> charsets() {
-
- final ArrayList<String> ks;
- synchronized (this) {
- init();
- ks = new ArrayList<>(classMap.keySet());
- }
-
- return new Iterator<Charset>() {
- Iterator<String> i = ks.iterator();
-
- public boolean hasNext() {
- return i.hasNext();
- }
-
- public Charset next() {
- String csn = i.next();
- synchronized (AbstractCharsetProvider.this) {
- return lookup(csn);
- }
- }
-
- public void remove() {
- throw new UnsupportedOperationException();
- }
- };
- }
-
- public final String[] aliases(String charsetName) {
- synchronized (this) {
- init();
- return aliasNameMap.get(charsetName);
- }
- }
-
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/AppInputStream.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/AppInputStream.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,41 +26,54 @@
package sun.security.ssl;
-import java.io.*;
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import javax.net.ssl.SSLProtocolException;
/**
* InputStream for application data as returned by SSLSocket.getInputStream().
- * It uses an InputRecord as internal buffer that is refilled on demand
- * whenever it runs out of data.
*
* @author David Brownell
*/
-class AppInputStream extends InputStream {
+final class AppInputStream extends InputStream {
+ // the buffer size for each read of network data
+ private static final int READ_BUFFER_SIZE = 4096;
// static dummy array we use to implement skip()
- private final static byte[] SKIP_ARRAY = new byte[1024];
+ private static final byte[] SKIP_ARRAY = new byte[256];
+
+ // the related socket of the input stream
+ private final SSLSocketImpl socket;
- private SSLSocketImpl c;
- InputRecord r;
+ // the temporary buffer used to read network
+ private ByteBuffer buffer;
+
+ // Is application data available in the stream?
+ private boolean appDataIsAvailable;
// One element array used to implement the single byte read() method
private final byte[] oneByte = new byte[1];
AppInputStream(SSLSocketImpl conn) {
- r = new InputRecord();
- c = conn;
+ this.buffer = ByteBuffer.allocate(READ_BUFFER_SIZE);
+ this.socket = conn;
+ this.appDataIsAvailable = false;
}
/**
* Return the minimum number of bytes that can be read without blocking.
+ *
* Currently not synchronized.
*/
@Override
public int available() throws IOException {
- if (c.checkEOF() || (r.isAppDataValid() == false)) {
+ if ((!appDataIsAvailable) || socket.checkEOF()) {
return 0;
}
- return r.available();
+
+ return buffer.remaining();
}
/**
@@ -72,17 +85,21 @@
if (n <= 0) { // EOF
return -1;
}
- return oneByte[0] & 0xff;
+ return oneByte[0] & 0xFF;
}
/**
- * Read up to "len" bytes into this buffer, starting at "off".
+ * Reads up to {@code len} bytes of data from the input stream into an
+ * array of bytes. An attempt is made to read as many as {@code len} bytes,
+ * but a smaller number may be read. The number of bytes actually read
+ * is returned as an integer.
+ *
* If the layer above needs more data, it asks for more, so we
* are responsible only for blocking to fill at most one buffer,
* and returning "-1" on non-fault EOF status.
*/
@Override
- public synchronized int read(byte b[], int off, int len)
+ public synchronized int read(byte[] b, int off, int len)
throws IOException {
if (b == null) {
throw new NullPointerException();
@@ -92,28 +109,69 @@
return 0;
}
- if (c.checkEOF()) {
+ if (socket.checkEOF()) {
return -1;
}
+
+ // Read the available bytes at first.
+ int remains = available();
+ if (remains > 0) {
+ int howmany = Math.min(remains, len);
+ buffer.get(b, off, howmany);
+
+ return howmany;
+ }
+
+ appDataIsAvailable = false;
+ int volume = 0;
+
try {
/*
* Read data if needed ... notice that the connection guarantees
* that handshake, alert, and change cipher spec data streams are
* handled as they arrive, so we never see them here.
*/
- while (r.available() == 0) {
- c.readDataRecord(r);
- if (c.checkEOF()) {
+ while(volume == 0) {
+ // Clear the buffer for a new record reading.
+ buffer.clear();
+
+ //
+ // grow the buffer if needed
+ //
+
+ // Read the header of a record into the buffer, and return
+ // the packet size.
+ int packetLen = socket.bytesInCompletePacket();
+ if (packetLen < 0) { // EOF
return -1;
}
+
+ // Is this packet bigger than SSL/TLS normally allows?
+ if (packetLen > SSLRecord.maxLargeRecordSize) {
+ throw new SSLProtocolException(
+ "Illegal packet size: " + packetLen);
+ }
+
+ if (packetLen > buffer.remaining()) {
+ buffer = ByteBuffer.allocate(packetLen);
+ }
+
+ volume = socket.readRecord(buffer);
+ if (volume < 0) { // EOF
+ return -1;
+ } else if (volume > 0) {
+ appDataIsAvailable = true;
+ break;
+ }
}
- int howmany = Math.min(len, r.available());
- howmany = r.read(b, off, howmany);
+ int howmany = Math.min(len, volume);
+ buffer.get(b, off, howmany);
return howmany;
} catch (Exception e) {
// shutdown and rethrow (wrapped) exception as appropriate
- c.handleException(e);
+ socket.handleException(e);
+
// dummy for compiler
return -1;
}
@@ -147,9 +205,8 @@
*/
@Override
public void close() throws IOException {
- c.close();
+ socket.close();
}
// inherit default mark/reset behavior (throw Exceptions) from InputStream
-
}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/AppOutputStream.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/AppOutputStream.java Thu Jun 04 18:49:37 2015 -0700
@@ -23,41 +23,33 @@
* questions.
*/
-
package sun.security.ssl;
import java.io.OutputStream;
import java.io.IOException;
+import java.nio.ByteBuffer;
/*
- * Output stream for application data. This is the kind of stream
- * that's handed out via SSLSocket.getOutputStream(). It's all the application
- * ever sees.
- *
- * Once the initial handshake has completed, application data may be
- * interleaved with handshake data. That is handled internally and remains
- * transparent to the application.
+ * OutputStream for application data as returned by SSLSocket.getOutputStream().
*
* @author David Brownell
*/
class AppOutputStream extends OutputStream {
- private SSLSocketImpl c;
- OutputRecord r;
+ private SSLSocketImpl socket;
// One element array used to implement the write(byte) method
private final byte[] oneByte = new byte[1];
AppOutputStream(SSLSocketImpl conn) {
- r = new OutputRecord(Record.ct_application_data);
- c = conn;
+ this.socket = conn;
}
/**
* Write the data out, NOW.
*/
@Override
- synchronized public void write(byte b[], int off, int len)
+ synchronized public void write(byte[] b, int off, int len)
throws IOException {
if (b == null) {
throw new NullPointerException();
@@ -68,64 +60,15 @@
}
// check if the Socket is invalid (error or closed)
- c.checkWrite();
+ socket.checkWrite();
- /*
- * By default, we counter chosen plaintext issues on CBC mode
- * ciphersuites in SSLv3/TLS1.0 by sending one byte of application
- * data in the first record of every payload, and the rest in
- * subsequent record(s). Note that the issues have been solved in
- * TLS 1.1 or later.
- *
- * It is not necessary to split the very first application record of
- * a freshly negotiated TLS session, as there is no previous
- * application data to guess. To improve compatibility, we will not
- * split such records.
- *
- * This avoids issues in the outbound direction. For a full fix,
- * the peer must have similar protections.
- */
- boolean isFirstRecordOfThePayload = true;
-
- // Always flush at the end of each application level record.
- // This lets application synchronize read and write streams
- // however they like; if we buffered here, they couldn't.
+ // Delegate the writing to the underlying socket.
try {
- do {
- boolean holdRecord = false;
- int howmuch;
- if (isFirstRecordOfThePayload && c.needToSplitPayload()) {
- howmuch = Math.min(0x01, r.availableDataBytes());
- /*
- * Nagle's algorithm (TCP_NODELAY) was coming into
- * play here when writing short (split) packets.
- * Signal to the OutputRecord code to internally
- * buffer this small packet until the next outbound
- * packet (of any type) is written.
- */
- if ((len != 1) && (howmuch == 1)) {
- holdRecord = true;
- }
- } else {
- howmuch = Math.min(len, r.availableDataBytes());
- }
-
- if (isFirstRecordOfThePayload && howmuch != 0) {
- isFirstRecordOfThePayload = false;
- }
-
- // NOTE: *must* call c.writeRecord() even for howmuch == 0
- if (howmuch > 0) {
- r.write(b, off, howmuch);
- off += howmuch;
- len -= howmuch;
- }
- c.writeRecord(r, holdRecord);
- c.checkWrite();
- } while (len > 0);
+ socket.writeRecord(b, off, len);
+ socket.checkWrite();
} catch (Exception e) {
// shutdown and rethrow (wrapped) exception as appropriate
- c.handleException(e);
+ socket.handleException(e);
}
}
@@ -143,7 +86,7 @@
*/
@Override
public void close() throws IOException {
- c.close();
+ socket.close();
}
// inherit no-op flush()
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Authenticator.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Authenticator.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,19 +28,26 @@
import java.util.Arrays;
/**
- * This class represents an SSL/TLS message authentication token,
+ * This class represents an SSL/TLS/DTLS message authentication token,
* which encapsulates a sequence number and ensures that attempts to
* delete or reorder messages can be detected.
*
- * Each SSL/TLS connection state contains a sequence number, which
- * is maintained separately for read and write states. The sequence
- * number MUST be set to zero whenever a connection state is made the
- * active state. Sequence numbers are of type uint64 and may not
- * exceed 2^64-1. Sequence numbers do not wrap. If a SSL/TLS
- * implementation would need to wrap a sequence number, it must
- * renegotiate instead. A sequence number is incremented after each
- * record: specifically, the first record transmitted under a
- * particular connection state MUST use sequence number 0.
+ * Each connection state contains a sequence number, which is maintained
+ * separately for read and write states.
+ *
+ * For SSL/TLS protocols, the sequence number MUST be set to zero
+ * whenever a connection state is made the active state.
+ *
+ * DTLS uses an explicit sequence number, rather than an implicit one.
+ * Sequence numbers are maintained separately for each epoch, with
+ * each sequence number initially being 0 for each epoch. The sequence
+ * number used to compute the DTLS MAC is the 64-bit value formed by
+ * concatenating the epoch and the sequence number.
+ *
+ * Sequence numbers do not wrap. If an implementation would need to wrap
+ * a sequence number, it must renegotiate instead. A sequence number is
+ * incremented after each record: specifically, the first record transmitted
+ * under a particular connection state MUST use sequence number 0.
*/
class Authenticator {
@@ -56,13 +63,30 @@
// sequence number + record type + protocol version + record length
private static final int BLOCK_SIZE_TLS = 8 + 1 + 2 + 2;
+ // the block size of DTLS v1.0 and later:
+ // epoch + sequence number + record type + protocol version + record length
+ private static final int BLOCK_SIZE_DTLS = 2 + 6 + 1 + 2 + 2;
+
+ private final boolean isDTLS;
+
/**
* Default construct, no message authentication token is initialized.
*
* Note that this construct can only be called for null MAC
*/
- Authenticator() {
- block = new byte[0];
+ protected Authenticator(boolean isDTLS) {
+ if (isDTLS) {
+ // For DTLS protocols, plaintexts use explicit epoch and
+ // sequence number in each record. The first 8 byte of
+ // the block is initialized for null MAC so that the
+ // epoch and sequence number can be acquired to generate
+ // plaintext records.
+ block = new byte[8];
+ } else {
+ block = new byte[0];
+ }
+
+ this.isDTLS = isDTLS;
}
/**
@@ -70,12 +94,22 @@
* SSL/TLS protocol.
*/
Authenticator(ProtocolVersion protocolVersion) {
- if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+ if (protocolVersion.isDTLSProtocol()) {
+ block = new byte[BLOCK_SIZE_DTLS];
+ block[9] = protocolVersion.major;
+ block[10] = protocolVersion.minor;
+
+ this.isDTLS = true;
+ } else if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
block = new byte[BLOCK_SIZE_TLS];
block[9] = protocolVersion.major;
block[10] = protocolVersion.minor;
+
+ this.isDTLS = false;
} else {
block = new byte[BLOCK_SIZE_SSL];
+
+ this.isDTLS = false;
}
}
@@ -93,11 +127,19 @@
* Conservatively, we don't allow more records to be generated
* when there are only 2^8 sequence numbers left.
*/
- return (block.length != 0 &&
+ if (isDTLS) {
+ return (block.length != 0 &&
+ // no epoch bytes, block[0] and block[1]
+ block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
+ block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
+ block[6] == (byte)0xFF);
+ } else {
+ return (block.length != 0 &&
block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
block[6] == (byte)0xFF);
+ }
}
/**
@@ -113,14 +155,22 @@
final boolean seqNumIsHuge() {
/*
* Conservatively, we should ask for renegotiation when there are
- * only 2^48 sequence numbers left.
+ * only 2^32 sequence numbers left.
*/
- return (block.length != 0 &&
- block[0] == (byte)0xFF && block[1] == (byte)0xFF);
+ if (isDTLS) {
+ return (block.length != 0 &&
+ // no epoch bytes, block[0] and block[1]
+ block[2] == (byte)0xFF && block[3] == (byte)0xFF);
+ } else {
+ return (block.length != 0 &&
+ block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
+ block[2] == (byte)0xFF && block[3] == (byte)0xFF);
+ }
}
/**
- * Gets the current sequence number.
+ * Gets the current sequence number, including the epoch number for
+ * DTLS protocols.
*
* @return the byte array of the current sequence number
*/
@@ -129,33 +179,83 @@
}
/**
+ * Sets the epoch number (only apply to DTLS protocols).
+ */
+ final void setEpochNumber(int epoch) {
+ if (!isDTLS) {
+ throw new RuntimeException(
+ "Epoch numbers apply to DTLS protocols only");
+ }
+
+ block[0] = (byte)((epoch >> 8) & 0xFF);
+ block[1] = (byte)(epoch & 0xFF);
+ }
+
+ /**
+ * Increase the sequence number.
+ */
+ final void increaseSequenceNumber() {
+ /*
+ * The sequence number in the block array is a 64-bit
+ * number stored in big-endian format.
+ */
+ int k = 7;
+ while ((k >= 0) && (++block[k] == 0)) {
+ k--;
+ }
+ }
+
+ /**
* Acquires the current message authentication information with the
* specified record type and fragment length, and then increases the
* sequence number.
*
* @param type the record type
* @param length the fragment of the record
+ * @param sequence the explicit sequence number of the record
+ *
* @return the byte array of the current message authentication information
*/
- final byte[] acquireAuthenticationBytes(byte type, int length) {
+ final byte[] acquireAuthenticationBytes(
+ byte type, int length, byte[] sequence) {
+
byte[] copy = block.clone();
+ if (sequence != null) {
+ if (sequence.length != 8) {
+ throw new RuntimeException(
+ "Insufficient explicit sequence number bytes");
+ }
+
+ System.arraycopy(sequence, 0, copy, 0, sequence.length);
+ } // Otherwise, use the implicit sequence number.
if (block.length != 0) {
copy[8] = type;
+
copy[copy.length - 2] = (byte)(length >> 8);
copy[copy.length - 1] = (byte)(length);
- /*
- * Increase the sequence number in the block array
- * it is a 64-bit number stored in big-endian format
- */
- int k = 7;
- while ((k >= 0) && (++block[k] == 0)) {
- k--;
+ if (sequence == null || sequence.length != 0) {
+ // Increase the implicit sequence number in the block array.
+ increaseSequenceNumber();
}
}
return copy;
}
+ final static long toLong(byte[] recordEnS) {
+ if (recordEnS != null && recordEnS.length == 8) {
+ return ((recordEnS[0] & 0xFFL) << 56) |
+ ((recordEnS[1] & 0xFFL) << 48) |
+ ((recordEnS[2] & 0xFFL) << 40) |
+ ((recordEnS[3] & 0xFFL) << 32) |
+ ((recordEnS[4] & 0xFFL) << 24) |
+ ((recordEnS[5] & 0xFFL) << 16) |
+ ((recordEnS[6] & 0xFFL) << 8) |
+ (recordEnS[7] & 0xFFL);
+ }
+
+ return -1L;
+ }
}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java Thu Jun 04 18:49:37 2015 -0700
@@ -154,9 +154,9 @@
* NULL cipherbox. Identity operation, no encryption.
*/
private CipherBox() {
- this.protocolVersion = ProtocolVersion.DEFAULT;
+ this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
this.cipher = null;
- this.cipherType = STREAM_CIPHER;
+ this.cipherType = NULL_CIPHER;
this.fixedIv = new byte[0];
this.key = null;
this.mode = Cipher.ENCRYPT_MODE; // choose at random
@@ -197,7 +197,7 @@
*/
if (iv == null && bulkCipher.ivSize != 0 &&
mode == Cipher.DECRYPT_MODE &&
- protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ protocolVersion.useTLS11PlusSpec()) {
iv = getFixedMask(bulkCipher.ivSize);
}
@@ -491,7 +491,7 @@
newLen = removePadding(
buf, offset, newLen, tagLen, blockSize, protocolVersion);
- if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ if (protocolVersion.useTLS11PlusSpec()) {
if (newLen < blockSize) {
throw new BadPaddingException("invalid explicit IV");
}
@@ -573,7 +573,7 @@
newLen = removePadding(bb, tagLen, blockSize, protocolVersion);
// check the explicit IV of TLS v1.1 or later
- if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ if (protocolVersion.useTLS11PlusSpec()) {
if (newLen < blockSize) {
throw new BadPaddingException("invalid explicit IV");
}
@@ -746,7 +746,7 @@
// The padding data should be filled with the padding length value.
int[] results = checkPadding(buf, offset + newLen,
padLen + 1, (byte)(padLen & 0xFF));
- if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+ if (protocolVersion.useTLS10PlusSpec()) {
if (results[0] != 0) { // padding data has invalid bytes
throw new BadPaddingException("Invalid TLS padding data");
}
@@ -792,7 +792,7 @@
int[] results = checkPadding(
bb.duplicate().position(offset + newLen),
(byte)(padLen & 0xFF));
- if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+ if (protocolVersion.useTLS10PlusSpec()) {
if (results[0] != 0) { // padding data has invalid bytes
throw new BadPaddingException("Invalid TLS padding data");
}
@@ -873,7 +873,7 @@
// For block ciphers, the explicit IV length is of length
// SecurityParameters.record_iv_length, which is equal to
// the SecurityParameters.block_size.
- if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ if (protocolVersion.useTLS11PlusSpec()) {
return cipher.getBlockSize();
}
break;
@@ -902,7 +902,7 @@
* @return the explicit nonce size of the cipher.
*/
int applyExplicitNonce(Authenticator authenticator, byte contentType,
- ByteBuffer bb) throws BadPaddingException {
+ ByteBuffer bb, byte[] sequence) throws BadPaddingException {
switch (cipherType) {
case BLOCK_CIPHER:
// sanity check length of the ciphertext
@@ -918,7 +918,7 @@
// For block ciphers, the explicit IV length is of length
// SecurityParameters.record_iv_length, which is equal to
// the SecurityParameters.block_size.
- if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ if (protocolVersion.useTLS11PlusSpec()) {
return cipher.getBlockSize();
}
break;
@@ -945,7 +945,8 @@
// update the additional authentication data
byte[] aad = authenticator.acquireAuthenticationBytes(
- contentType, bb.remaining() - recordIvSize - tagSize);
+ contentType, bb.remaining() - recordIvSize - tagSize,
+ sequence);
cipher.updateAAD(aad);
return recordIvSize;
@@ -957,33 +958,6 @@
}
/*
- * Applies the explicit nonce/IV to this cipher. This method is used to
- * decrypt an SSL/TLS input record.
- *
- * The returned value is the SecurityParameters.record_iv_length in
- * RFC 4346/5246. It is the size of explicit IV for CBC mode, and the
- * size of explicit nonce for AEAD mode.
- *
- * @param authenticator the authenticator to get the additional
- * authentication data
- * @param contentType the content type of the input record
- * @param buf the byte array to get the explicit nonce from
- * @param offset the offset of the byte buffer
- * @param cipheredLength the ciphered fragment length of the output
- * record, it is the TLSCiphertext.length in RFC 4346/5246.
- *
- * @return the explicit nonce size of the cipher.
- */
- int applyExplicitNonce(Authenticator authenticator,
- byte contentType, byte[] buf, int offset,
- int cipheredLength) throws BadPaddingException {
-
- ByteBuffer bb = ByteBuffer.wrap(buf, offset, cipheredLength);
-
- return applyExplicitNonce(authenticator, contentType, bb);
- }
-
- /*
* Creates the explicit nonce/IV to this cipher. This method is used to
* encrypt an SSL/TLS output record.
*
@@ -1005,7 +979,7 @@
byte[] nonce = new byte[0];
switch (cipherType) {
case BLOCK_CIPHER:
- if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ if (protocolVersion.useTLS11PlusSpec()) {
// For block ciphers, the explicit IV length is of length
// SecurityParameters.record_iv_length, which is equal to
// the SecurityParameters.block_size.
@@ -1034,9 +1008,10 @@
"invalid key or spec in GCM mode", ikae);
}
- // update the additional authentication data
+ // Update the additional authentication data, using the
+ // implicit sequence number of the authenticator.
byte[] aad = authenticator.acquireAuthenticationBytes(
- contentType, fragmentLength);
+ contentType, fragmentLength, null);
cipher.updateAAD(aad);
break;
}
@@ -1044,6 +1019,93 @@
return nonce;
}
+ // See also CipherSuite.calculatePacketSize().
+ int calculatePacketSize(int fragmentSize, int macLen, int headerSize) {
+ int packetSize = fragmentSize;
+ if (cipher != null) {
+ int blockSize = cipher.getBlockSize();
+ switch (cipherType) {
+ case BLOCK_CIPHER:
+ packetSize += macLen;
+ packetSize += 1; // 1 byte padding length field
+ packetSize += // use the minimal padding
+ (blockSize - (packetSize % blockSize)) % blockSize;
+ if (protocolVersion.useTLS11PlusSpec()) {
+ packetSize += blockSize; // explicit IV
+ }
+
+ break;
+ case AEAD_CIPHER:
+ packetSize += recordIvSize;
+ packetSize += tagSize;
+
+ break;
+ default: // NULL_CIPHER or STREAM_CIPHER
+ packetSize += macLen;
+ }
+ }
+
+ return packetSize + headerSize;
+ }
+
+ // See also CipherSuite.calculateFragSize().
+ int calculateFragmentSize(int packetLimit, int macLen, int headerSize) {
+ int fragLen = packetLimit - headerSize;
+ if (cipher != null) {
+ int blockSize = cipher.getBlockSize();
+ switch (cipherType) {
+ case BLOCK_CIPHER:
+ if (protocolVersion.useTLS11PlusSpec()) {
+ fragLen -= blockSize; // explicit IV
+ }
+ fragLen -= (fragLen % blockSize); // cannot hold a block
+ // No padding for a maximum fragment.
+ fragLen -= 1; // 1 byte padding length field: 0x00
+ fragLen -= macLen;
+
+ break;
+ case AEAD_CIPHER:
+ fragLen -= recordIvSize;
+ fragLen -= tagSize;
+
+ break;
+ default: // NULL_CIPHER or STREAM_CIPHER
+ fragLen -= macLen;
+ }
+ }
+
+ return fragLen;
+ }
+
+ // Estimate the maximum fragment size of a received packet.
+ int estimateFragmentSize(int packetSize, int macLen, int headerSize) {
+ int fragLen = packetSize - headerSize;
+ if (cipher != null) {
+ int blockSize = cipher.getBlockSize();
+ switch (cipherType) {
+ case BLOCK_CIPHER:
+ if (protocolVersion.useTLS11PlusSpec()) {
+ fragLen -= blockSize; // explicit IV
+ }
+ // No padding for a maximum fragment.
+ fragLen -= 1; // 1 byte padding length field: 0x00
+ fragLen -= macLen;
+
+ break;
+ case AEAD_CIPHER:
+ fragLen -= recordIvSize;
+ fragLen -= tagSize;
+
+ break;
+ default: // NULL_CIPHER or STREAM_CIPHER
+ fragLen -= macLen;
+ }
+ }
+
+ return fragLen;
+ }
+
+
/*
* Is this cipher available?
*
@@ -1100,7 +1162,7 @@
if ((fragmentLen % blockSize) == 0) {
int minimal = tagLen + 1;
minimal = (minimal >= blockSize) ? minimal : blockSize;
- if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ if (protocolVersion.useTLS11PlusSpec()) {
minimal += blockSize; // plus the size of the explicit IV
}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java Thu Jun 04 18:49:37 2015 -0700
@@ -122,9 +122,15 @@
final boolean allowed;
// obsoleted since protocol version
+ //
+ // TLS version is used. If checking DTLS versions, please map to
+ // TLS version firstly. See ProtocolVersion.mapToTLSProtocol().
final int obsoleted;
- // supported since protocol version
+ // supported since protocol version (TLS version is used)
+ //
+ // TLS version is used. If checking DTLS versions, please map to
+ // TLS version firstly. See ProtocolVersion.mapToTLSProtocol().
final int supported;
/**
@@ -182,6 +188,70 @@
return this != C_SCSV && isAvailable();
}
+ // See also CipherBox.calculatePacketSize().
+ int calculatePacketSize(int fragmentSize,
+ ProtocolVersion protocolVersion, boolean isDTLS) {
+
+ int packetSize = fragmentSize;
+ if (cipher != B_NULL) {
+ int blockSize = cipher.ivSize;
+ switch (cipher.cipherType) {
+ case BLOCK_CIPHER:
+ packetSize += macAlg.size;
+ packetSize += 1; // 1 byte padding length field
+ packetSize += // use the minimal padding
+ (blockSize - (packetSize % blockSize)) % blockSize;
+ if (protocolVersion.useTLS11PlusSpec()) {
+ packetSize += blockSize; // explicit IV
+ }
+
+ break;
+ case AEAD_CIPHER:
+ packetSize += cipher.ivSize - cipher.fixedIvSize; // record IV
+ packetSize += cipher.tagSize;
+
+ break;
+ default: // NULL_CIPHER or STREAM_CIPHER
+ packetSize += macAlg.size;
+ }
+ }
+
+ return packetSize +
+ (isDTLS ? DTLSRecord.headerSize : SSLRecord.headerSize);
+ }
+
+ // See also CipherBox.calculateFragmentSize().
+ int calculateFragSize(int packetLimit,
+ ProtocolVersion protocolVersion, boolean isDTLS) {
+
+ int fragSize = packetLimit -
+ (isDTLS ? DTLSRecord.headerSize : SSLRecord.headerSize);
+ if (cipher != B_NULL) {
+ int blockSize = cipher.ivSize;
+ switch (cipher.cipherType) {
+ case BLOCK_CIPHER:
+ if (protocolVersion.useTLS11PlusSpec()) {
+ fragSize -= blockSize; // explicit IV
+ }
+ fragSize -= (fragSize % blockSize); // cannot hold a block
+ // No padding for a maximum fragment.
+ fragSize -= 1; // 1 byte padding length field: 0x00
+ fragSize -= macAlg.size;
+
+ break;
+ case AEAD_CIPHER:
+ fragSize -= cipher.tagSize;
+ fragSize -= cipher.ivSize - cipher.fixedIvSize; // record IV
+
+ break;
+ default: // NULL_CIPHER or STREAM_CIPHER
+ fragSize -= macAlg.size;
+ }
+ }
+
+ return fragSize;
+ }
+
/**
* Compares CipherSuites based on their priority. Has the effect of
* sorting CipherSuites when put in a sorted collection, which is
@@ -242,7 +312,7 @@
return c;
}
- // for use by CipherSuiteList only
+ // for use by SSLContextImpl only
static Collection<CipherSuite> allowedCipherSuites() {
return nameMap.values();
}
@@ -372,7 +442,8 @@
}
static enum CipherType {
- STREAM_CIPHER, // null or stream cipher
+ NULL_CIPHER, // null cipher
+ STREAM_CIPHER, // stream cipher
BLOCK_CIPHER, // block cipher in CBC mode
AEAD_CIPHER // AEAD cipher
}
@@ -387,7 +458,7 @@
static enum BulkCipher {
// export strength ciphers
- B_NULL("NULL", STREAM_CIPHER, 0, 0, 0, 0, true),
+ B_NULL("NULL", NULL_CIPHER, 0, 0, 0, 0, true),
B_RC4_40(CIPHER_RC4, STREAM_CIPHER, 5, 16, 0, 0, true),
B_RC2_40("RC2", BLOCK_CIPHER, 5, 16, 8, 0, false),
B_DES_40(CIPHER_DES, BLOCK_CIPHER, 5, 8, 8, 0, true),
@@ -568,7 +639,7 @@
iv = new IvParameterSpec(new byte[cipher.ivSize]);
}
temporary = cipher.newCipher(
- ProtocolVersion.DEFAULT,
+ ProtocolVersion.DEFAULT_TLS,
key, iv, secureRandom, true);
b = temporary.isAvailable();
} catch (NoSuchAlgorithmException e) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Ciphertext.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import static sun.security.ssl.HandshakeMessage.*;
+
+/*
+ * enumeration of record type
+ */
+final class Ciphertext {
+ final static Ciphertext CIPHERTEXT_NULL = new Ciphertext();
+
+ RecordType recordType;
+ long recordSN;
+
+ HandshakeStatus handshakeStatus; // null if not used or not handshaking
+
+ Ciphertext() {
+ this.recordType = null;
+ this.recordSN = -1L;
+ this.handshakeStatus = null;
+ }
+
+ Ciphertext(RecordType recordType, long recordSN) {
+ this.recordType = recordType;
+ this.recordSN = recordSN;
+ this.handshakeStatus = null;
+ }
+
+ static enum RecordType {
+ RECORD_CHANGE_CIPHER_SPEC (
+ Record.ct_change_cipher_spec, ht_not_applicable),
+ RECORD_ALERT (
+ Record.ct_alert, ht_not_applicable),
+ RECORD_HELLO_REQUEST (
+ Record.ct_handshake, ht_hello_request),
+ RECORD_CLIENT_HELLO (
+ Record.ct_handshake, ht_client_hello),
+ RECORD_SERVER_HELLO (
+ Record.ct_handshake, ht_server_hello),
+ RECORD_HELLO_VERIFY_REQUEST (
+ Record.ct_handshake, ht_hello_verify_request),
+ RECORD_NEW_SESSION_TICKET (
+ Record.ct_handshake, ht_new_session_ticket),
+ RECORD_CERTIFICATE (
+ Record.ct_handshake, ht_certificate),
+ RECORD_SERVER_KEY_EXCHANGE (
+ Record.ct_handshake, ht_server_key_exchange),
+ RECORD_CERTIFICATE_REQUEST (
+ Record.ct_handshake, ht_certificate_request),
+ RECORD_SERVER_HELLO_DONE (
+ Record.ct_handshake, ht_server_hello_done),
+ RECORD_CERTIFICATE_VERIFY (
+ Record.ct_handshake, ht_certificate_verify),
+ RECORD_CLIENT_KEY_EXCHANGE (
+ Record.ct_handshake, ht_client_key_exchange),
+ RECORD_FINISHED (
+ Record.ct_handshake, ht_finished),
+ RECORD_CERTIFICATE_URL (
+ Record.ct_handshake, ht_certificate_url),
+ RECORD_CERTIFICATE_STATUS (
+ Record.ct_handshake, ht_certificate_status),
+ RECORD_SUPPLIEMENTAL_DATA (
+ Record.ct_handshake, ht_supplemental_data),
+ RECORD_APPLICATION_DATA (
+ Record.ct_application_data, ht_not_applicable);
+
+ byte contentType;
+ byte handshakeType;
+
+ private RecordType(byte contentType, byte handshakeType) {
+ this.contentType = contentType;
+ this.handshakeType = handshakeType;
+ }
+
+ static RecordType valueOf(byte contentType, byte handshakeType) {
+ if (contentType == Record.ct_change_cipher_spec) {
+ return RECORD_CHANGE_CIPHER_SPEC;
+ } else if (contentType == Record.ct_alert) {
+ return RECORD_ALERT;
+ } else if (contentType == Record.ct_application_data) {
+ return RECORD_APPLICATION_DATA;
+ } else if (handshakeType == ht_hello_request) {
+ return RECORD_HELLO_REQUEST;
+ } else if (handshakeType == ht_client_hello) {
+ return RECORD_CLIENT_HELLO;
+ } else if (handshakeType == ht_server_hello) {
+ return RECORD_SERVER_HELLO;
+ } else if (handshakeType == ht_hello_verify_request) {
+ return RECORD_HELLO_VERIFY_REQUEST;
+ } else if (handshakeType == ht_new_session_ticket) {
+ return RECORD_NEW_SESSION_TICKET;
+ } else if (handshakeType == ht_certificate) {
+ return RECORD_CERTIFICATE;
+ } else if (handshakeType == ht_server_key_exchange) {
+ return RECORD_SERVER_KEY_EXCHANGE;
+ } else if (handshakeType == ht_certificate_request) {
+ return RECORD_CERTIFICATE_REQUEST;
+ } else if (handshakeType == ht_server_hello_done) {
+ return RECORD_SERVER_HELLO_DONE;
+ } else if (handshakeType == ht_certificate_verify) {
+ return RECORD_CERTIFICATE_VERIFY;
+ } else if (handshakeType == ht_client_key_exchange) {
+ return RECORD_CLIENT_KEY_EXCHANGE;
+ } else if (handshakeType == ht_finished) {
+ return RECORD_FINISHED;
+ } else if (handshakeType == ht_certificate_url) {
+ return RECORD_CERTIFICATE_URL;
+ } else if (handshakeType == ht_certificate_status) {
+ return RECORD_CERTIFICATE_STATUS;
+ } else if (handshakeType == ht_supplemental_data) {
+ return RECORD_SUPPLIEMENTAL_DATA;
+ }
+
+ // otherwise, invalid record type
+ throw new IllegalArgumentException(
+ "Invalid record type (ContentType:" + contentType +
+ ", HandshakeType:" + handshakeType + ")");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientAuthType.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/**
+ * Client authentication type.
+ */
+enum ClientAuthType {
+ CLIENT_AUTH_NONE, // turn off client authentication
+ CLIENT_AUTH_REQUESTED, // need to request client authentication
+ CLIENT_AUTH_REQUIRED // require client authentication
+}
+
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Thu Jun 04 18:49:37 2015 -0700
@@ -44,8 +44,6 @@
import javax.net.ssl.*;
-import javax.security.auth.Subject;
-
import sun.security.ssl.HandshakeMessage.*;
import static sun.security.ssl.CipherSuite.KeyExchange.*;
@@ -141,11 +139,20 @@
private final static boolean allowUnsafeServerCertChange =
Debug.getBooleanProperty("jdk.tls.allowUnsafeServerCertChange", false);
+ // To switch off the max_fragment_length extension.
+ private final static boolean enableMFLExtension =
+ Debug.getBooleanProperty("jsse.enableMFLExtension", false);
+
private List<SNIServerName> requestedServerNames =
Collections.<SNIServerName>emptyList();
+ // maximum fragment length
+ private int requestedMFLength = -1; // -1: no fragment length limit
+
private boolean serverNamesAccepted = false;
+ private ClientHello initialClientHelloMsg = null; // DTLS only
+
/*
* the reserved server certificate chain in previous handshaking
*
@@ -172,11 +179,12 @@
ProtocolList enabledProtocols,
ProtocolVersion activeProtocolVersion,
boolean isInitialHandshake, boolean secureRenegotiation,
- byte[] clientVerifyData, byte[] serverVerifyData) {
+ byte[] clientVerifyData, byte[] serverVerifyData,
+ boolean isDTLS) {
super(engine, context, enabledProtocols, true, true,
activeProtocolVersion, isInitialHandshake, secureRenegotiation,
- clientVerifyData, serverVerifyData);
+ clientVerifyData, serverVerifyData, isDTLS);
}
/*
@@ -191,29 +199,48 @@
*/
@Override
void processMessage(byte type, int messageLen) throws IOException {
- if (state >= type
- && (type != HandshakeMessage.ht_hello_request)) {
- throw new SSLProtocolException(
- "Handshake message sequence violation, " + type);
- }
+ // check the handshake state
+ handshakeState.check(type);
switch (type) {
case HandshakeMessage.ht_hello_request:
- this.serverHelloRequest(new HelloRequest(input));
+ HelloRequest helloRequest = new HelloRequest(input);
+ handshakeState.update(helloRequest, resumingSession);
+ this.serverHelloRequest(helloRequest);
+ break;
+
+ case HandshakeMessage.ht_hello_verify_request:
+ if (!isDTLS) {
+ throw new SSLProtocolException(
+ "hello_verify_request is not a SSL/TLS handshake message");
+ }
+
+ HelloVerifyRequest helloVerifyRequest =
+ new HelloVerifyRequest(input, messageLen);
+ handshakeState.update(helloVerifyRequest, resumingSession);
+ this.helloVerifyRequest(helloVerifyRequest);
break;
case HandshakeMessage.ht_server_hello:
- this.serverHello(new ServerHello(input, messageLen));
+ ServerHello serverHello = new ServerHello(input, messageLen);
+ this.serverHello(serverHello);
+
+ // This handshake state update needs the resumingSession value
+ // set by serverHello().
+ handshakeState.update(serverHello, resumingSession);
break;
case HandshakeMessage.ht_certificate:
if (keyExchange == K_DH_ANON || keyExchange == K_ECDH_ANON
- || keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
+ || ClientKeyExchangeService.find(keyExchange.name) != null) {
+ // No external key exchange provider needs a cert now.
fatalSE(Alerts.alert_unexpected_message,
"unexpected server cert chain");
// NOTREACHED
}
- this.serverCertificate(new CertificateMsg(input));
+ CertificateMsg certificateMsg = new CertificateMsg(input);
+ handshakeState.update(certificateMsg, resumingSession);
+ this.serverCertificate(certificateMsg);
serverKey =
session.getPeerCertificates()[0].getPublicKey();
break;
@@ -249,41 +276,52 @@
}
try {
- this.serverKeyExchange(new RSA_ServerKeyExchange(input));
+ RSA_ServerKeyExchange rsaSrvKeyExchange =
+ new RSA_ServerKeyExchange(input);
+ handshakeState.update(rsaSrvKeyExchange, resumingSession);
+ this.serverKeyExchange(rsaSrvKeyExchange);
} catch (GeneralSecurityException e) {
- throwSSLException("Server key", e);
+ throw new SSLException("Server key", e);
}
break;
case K_DH_ANON:
try {
- this.serverKeyExchange(new DH_ServerKeyExchange(
- input, protocolVersion));
+ DH_ServerKeyExchange dhSrvKeyExchange =
+ new DH_ServerKeyExchange(input, protocolVersion);
+ handshakeState.update(dhSrvKeyExchange, resumingSession);
+ this.serverKeyExchange(dhSrvKeyExchange);
} catch (GeneralSecurityException e) {
- throwSSLException("Server key", e);
+ throw new SSLException("Server key", e);
}
break;
case K_DHE_DSS:
case K_DHE_RSA:
try {
- this.serverKeyExchange(new DH_ServerKeyExchange(
- input, serverKey,
- clnt_random.random_bytes, svr_random.random_bytes,
- messageLen,
- localSupportedSignAlgs, protocolVersion));
+ DH_ServerKeyExchange dhSrvKeyExchange =
+ new DH_ServerKeyExchange(
+ input, serverKey,
+ clnt_random.random_bytes, svr_random.random_bytes,
+ messageLen,
+ localSupportedSignAlgs, protocolVersion);
+ handshakeState.update(dhSrvKeyExchange, resumingSession);
+ this.serverKeyExchange(dhSrvKeyExchange);
} catch (GeneralSecurityException e) {
- throwSSLException("Server key", e);
+ throw new SSLException("Server key", e);
}
break;
case K_ECDHE_ECDSA:
case K_ECDHE_RSA:
case K_ECDH_ANON:
try {
- this.serverKeyExchange(new ECDH_ServerKeyExchange
- (input, serverKey, clnt_random.random_bytes,
- svr_random.random_bytes,
- localSupportedSignAlgs, protocolVersion));
+ ECDH_ServerKeyExchange ecdhSrvKeyExchange =
+ new ECDH_ServerKeyExchange
+ (input, serverKey, clnt_random.random_bytes,
+ svr_random.random_bytes,
+ localSupportedSignAlgs, protocolVersion);
+ handshakeState.update(ecdhSrvKeyExchange, resumingSession);
+ this.serverKeyExchange(ecdhSrvKeyExchange);
} catch (GeneralSecurityException e) {
- throwSSLException("Server key", e);
+ throw new SSLException("Server key", e);
}
break;
case K_RSA:
@@ -294,13 +332,9 @@
throw new SSLProtocolException(
"Protocol violation: server sent a server key exchange"
+ " message for key exchange " + keyExchange);
- case K_KRB5:
- case K_KRB5_EXPORT:
- throw new SSLProtocolException(
- "unexpected receipt of server key exchange algorithm");
default:
throw new SSLProtocolException(
- "unsupported key exchange algorithm = "
+ "unsupported or unexpected key exchange algorithm = "
+ keyExchange);
}
break;
@@ -311,17 +345,19 @@
throw new SSLHandshakeException(
"Client authentication requested for "+
"anonymous cipher suite.");
- } else if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
+ } else if (ClientKeyExchangeService.find(keyExchange.name) != null) {
+ // No external key exchange provider needs a cert now.
throw new SSLHandshakeException(
"Client certificate requested for "+
- "kerberos cipher suite.");
+ "external cipher suite: " + keyExchange);
}
certRequest = new CertificateRequest(input, protocolVersion);
if (debug != null && Debug.isOn("handshake")) {
certRequest.print(System.out);
}
+ handshakeState.update(certRequest, resumingSession);
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
Collection<SignatureAndHashAlgorithm> peerSignAlgs =
certRequest.getSignAlgorithms();
if (peerSignAlgs == null || peerSignAlgs.isEmpty()) {
@@ -345,33 +381,24 @@
break;
case HandshakeMessage.ht_server_hello_done:
- this.serverHelloDone(new ServerHelloDone(input));
+ ServerHelloDone serverHelloDone = new ServerHelloDone(input);
+ handshakeState.update(serverHelloDone, resumingSession);
+ this.serverHelloDone(serverHelloDone);
+
break;
case HandshakeMessage.ht_finished:
- // A ChangeCipherSpec record must have been received prior to
- // reception of the Finished message (RFC 5246, 7.4.9).
- if (!receivedChangeCipherSpec()) {
- fatalSE(Alerts.alert_handshake_failure,
- "Received Finished message before ChangeCipherSpec");
- }
+ Finished serverFinished =
+ new Finished(protocolVersion, input, cipherSuite);
+ handshakeState.update(serverFinished, resumingSession);
+ this.serverFinished(serverFinished);
- this.serverFinished(
- new Finished(protocolVersion, input, cipherSuite));
break;
default:
throw new SSLProtocolException(
"Illegal client handshake msg, " + type);
}
-
- //
- // Move state machine forward if the message handling
- // code didn't already do so
- //
- if (state < type) {
- state = type;
- }
}
/*
@@ -389,10 +416,10 @@
// Could be (e.g. at connection setup) that we already
// sent the "client hello" but the server's not seen it.
//
- if (state < HandshakeMessage.ht_client_hello) {
+ if (!clientHelloDelivered) {
if (!secureRenegotiation && !allowUnsafeRenegotiation) {
// renegotiation is not allowed.
- if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) {
+ if (activeProtocolVersion.useTLS10PlusSpec()) {
// response with a no_renegotiation warning,
warningSE(Alerts.alert_no_renegotiation);
@@ -428,6 +455,29 @@
}
}
+ private void helloVerifyRequest(
+ HelloVerifyRequest mesg) throws IOException {
+
+ if (debug != null && Debug.isOn("handshake")) {
+ mesg.print(System.out);
+ }
+
+ //
+ // Note that HelloVerifyRequest.server_version is used solely to
+ // indicate packet formatting, and not as part of version negotiation.
+ // Need not to check version values match for HelloVerifyRequest
+ // message.
+ //
+ initialClientHelloMsg.cookie = mesg.cookie.clone();
+
+ if (debug != null && Debug.isOn("handshake")) {
+ initialClientHelloMsg.print(System.out);
+ }
+
+ // deliver the ClientHello message with cookie
+ initialClientHelloMsg.write(output);
+ handshakeState.update(initialClientHelloMsg, resumingSession);
+ }
/*
* Server chooses session parameters given options created by the
@@ -441,6 +491,9 @@
* probably authentication getting done.
*/
private void serverHello(ServerHello mesg) throws IOException {
+ // Dispose the reserved ClientHello message (if exists).
+ initialClientHelloMsg = null;
+
serverKeyExchangeReceived = false;
if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out);
@@ -536,7 +589,7 @@
}
setCipherSuite(mesg.cipherSuite);
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());
}
@@ -569,51 +622,22 @@
}
// validate subject identity
- if (sessionSuite.keyExchange == K_KRB5 ||
- sessionSuite.keyExchange == K_KRB5_EXPORT) {
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find(sessionSuite.keyExchange.name);
+ if (p != null) {
Principal localPrincipal = session.getLocalPrincipal();
- Subject subject = null;
- try {
- subject = AccessController.doPrivileged(
- new PrivilegedExceptionAction<Subject>() {
- @Override
- public Subject run() throws Exception {
- return Krb5Helper.getClientSubject(getAccSE());
- }});
- } catch (PrivilegedActionException e) {
- subject = null;
- if (debug != null && Debug.isOn("session")) {
- System.out.println("Attempt to obtain" +
- " subject failed!");
- }
- }
-
- if (subject != null) {
- // Eliminate dependency on KerberosPrincipal
- Set<Principal> principals =
- subject.getPrincipals(Principal.class);
- if (!principals.contains(localPrincipal)) {
- throw new SSLProtocolException("Server resumed" +
- " session with wrong subject identity");
- } else {
- if (debug != null && Debug.isOn("session"))
- System.out.println("Subject identity is same");
- }
+ if (p.isRelated(true, getAccSE(), localPrincipal)) {
+ if (debug != null && Debug.isOn("session"))
+ System.out.println("Subject identity is same");
} else {
- if (debug != null && Debug.isOn("session"))
- System.out.println("Kerberos credentials are not" +
- " present in the current Subject; check if " +
- " javax.security.auth.useSubjectAsCreds" +
- " system property has been set to false");
- throw new SSLProtocolException
- ("Server resumed session with no subject");
+ throw new SSLProtocolException("Server resumed" +
+ " session with wrong subject identity or no subject");
}
}
- // looks fine; resume it, and update the state machine.
+ // looks fine; resume it.
resumingSession = true;
- state = HandshakeMessage.ht_finished - 1;
calculateConnectionKeys(session.getMasterSecret());
if (debug != null && Debug.isOn("session")) {
System.out.println("%% Server resumed " + session);
@@ -627,6 +651,24 @@
}
}
+ // check the "max_fragment_length" extension
+ MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension)
+ mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
+ if (maxFragLenExt != null) {
+ if ((requestedMFLength == -1) ||
+ maxFragLenExt.getMaxFragLen() != requestedMFLength) {
+ // If the client did not request this extension, or the
+ // response value is different from the length it requested,
+ // abort the handshake with a fatal illegal_parameter alert.
+ fatalSE(Alerts.alert_illegal_parameter,
+ "Failed to negotiate the max_fragment_length");
+ }
+ } else if (!resumingSession) {
+ // no "max_fragment_length" extension
+ requestedMFLength = -1;
+ } // Otherwise, using the value negotiated during the original
+ // session initiation
+
if (resumingSession && session != null) {
setHandshakeSessionSE(session);
// Reserve the handshake state if this is a session-resumption
@@ -657,6 +699,8 @@
getLocalSupportedSignAlgs(),
mesg.sessionId, getHostSE(), getPortSE());
session.setRequestedServerNames(requestedServerNames);
+ session.setNegotiatedMaxFragSize(requestedMFLength);
+ session.setMaximumPacketSize(maximumPacketSize);
setHandshakeSessionSE(session);
if (debug != null && Debug.isOn("handshake")) {
System.out.println("** " + cipherSuite);
@@ -681,7 +725,6 @@
ephemeralServerKey = mesg.getPublicKey();
}
-
/*
* Diffie-Hellman key exchange. We save the server public key and
* our own D-H algorithm object so we can defer key calculations
@@ -716,13 +759,6 @@
if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out);
}
- /*
- * Always make sure the input has been digested before we
- * start emitting data, to ensure the hashes are correctly
- * computed for the Finished and CertificateVerify messages
- * which we send (here).
- */
- input.digestNow();
/*
* FIRST ... if requested, send an appropriate Certificate chain
@@ -817,7 +853,7 @@
// server. For SSLv3, send the no_certificate alert;
// TLS uses an empty cert chain instead.
//
- if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+ if (protocolVersion.useTLS10PlusSpec()) {
m1 = new CertificateMsg(new X509Certificate [0]);
} else {
warningSE(Alerts.alert_no_certificate);
@@ -837,6 +873,7 @@
m1.print(System.out);
}
m1.write(output);
+ handshakeState.update(m1, resumingSession);
}
}
@@ -943,8 +980,14 @@
ecdh = new ECDHCrypt(params, sslContext.getSecureRandom());
m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
break;
- case K_KRB5:
- case K_KRB5_EXPORT:
+ default:
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find(keyExchange.name);
+ if (p == null) {
+ // somethings very wrong
+ throw new RuntimeException
+ ("Unsupported key exchange: " + keyExchange);
+ }
String sniHostname = null;
for (SNIServerName serverName : requestedServerNames) {
if (serverName instanceof SNIHostName) {
@@ -953,13 +996,13 @@
}
}
- KerberosClientKeyExchange kerberosMsg = null;
+ ClientKeyExchange exMsg = null;
if (sniHostname != null) {
// use first requested SNI hostname
try {
- kerberosMsg = new KerberosClientKeyExchange(
- sniHostname, getAccSE(), protocolVersion,
- sslContext.getSecureRandom());
+ exMsg = p.createClientExchange(
+ sniHostname, getAccSE(), protocolVersion,
+ sslContext.getSecureRandom());
} catch(IOException e) {
if (serverNamesAccepted) {
// server accepted requested SNI hostname,
@@ -975,32 +1018,28 @@
}
}
- if (kerberosMsg == null) {
+ if (exMsg == null) {
String hostname = getHostSE();
if (hostname == null) {
throw new IOException("Hostname is required" +
- " to use Kerberos cipher suites");
+ " to use " + keyExchange + " key exchange");
}
- kerberosMsg = new KerberosClientKeyExchange(
- hostname, getAccSE(), protocolVersion,
- sslContext.getSecureRandom());
+ exMsg = p.createClientExchange(
+ hostname, getAccSE(), protocolVersion,
+ sslContext.getSecureRandom());
}
// Record the principals involved in exchange
- session.setPeerPrincipal(kerberosMsg.getPeerPrincipal());
- session.setLocalPrincipal(kerberosMsg.getLocalPrincipal());
- m2 = kerberosMsg;
+ session.setPeerPrincipal(exMsg.getPeerPrincipal());
+ session.setLocalPrincipal(exMsg.getLocalPrincipal());
+ m2 = exMsg;
break;
- default:
- // somethings very wrong
- throw new RuntimeException
- ("Unsupported key exchange: " + keyExchange);
}
if (debug != null && Debug.isOn("handshake")) {
m2.print(System.out);
}
m2.write(output);
-
+ handshakeState.update(m2, resumingSession);
/*
* THIRD, send a "change_cipher_spec" record followed by the
@@ -1010,8 +1049,6 @@
* to compute the "Finished" message, and to compute the keys used
* to protect all records following the change_cipher_spec.
*/
-
- output.doHashes();
output.flush();
/*
@@ -1027,13 +1064,6 @@
case K_RSA_EXPORT:
preMasterSecret = ((RSAClientKeyExchange)m2).preMaster;
break;
- case K_KRB5:
- case K_KRB5_EXPORT:
- byte[] secretBytes =
- ((KerberosClientKeyExchange)m2).getUnencryptedPreMasterSecret();
- preMasterSecret = new SecretKeySpec(secretBytes,
- "TlsPremasterSecret");
- break;
case K_DHE_RSA:
case K_DHE_DSS:
case K_DH_ANON:
@@ -1049,8 +1079,13 @@
preMasterSecret = ecdh.getAgreedSecret(serverKey);
break;
default:
- throw new IOException("Internal error: unknown key exchange "
- + keyExchange);
+ if (ClientKeyExchangeService.find(keyExchange.name) != null) {
+ preMasterSecret =
+ ((ClientKeyExchange) m2).clientKeyExchange();
+ } else {
+ throw new IOException("Internal error: unknown key exchange "
+ + keyExchange);
+ }
}
calculateKeys(preMasterSecret, null);
@@ -1069,7 +1104,7 @@
CertificateVerify m3;
try {
SignatureAndHashAlgorithm preferableSignatureAlgorithm = null;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm(
peerSupportedSignAlgs, signingKey.getAlgorithm(),
@@ -1103,13 +1138,17 @@
m3.print(System.out);
}
m3.write(output);
- output.doHashes();
+ handshakeState.update(m3, resumingSession);
+ output.flush();
}
/*
* OK, that's that!
*/
sendChangeCipherAndFinish(false);
+
+ // expecting the final ChangeCipherSpec and Finished messages
+ expectingFinishFlightSE();
}
@@ -1158,8 +1197,9 @@
* completed handshakes.
*/
if (resumingSession) {
- input.digestNow();
sendChangeCipherAndFinish(true);
+ } else {
+ handshakeFinished = true;
}
session.setLastAccessedTime(System.currentTimeMillis());
@@ -1188,6 +1228,10 @@
*/
private void sendChangeCipherAndFinish(boolean finishedTag)
throws IOException {
+
+ // Reload if this message has been reserved.
+ handshakeHash.reload();
+
Finished mesg = new Finished(protocolVersion, handshakeHash,
Finished.CLIENT, session.getMasterSecret(), cipherSuite);
@@ -1205,13 +1249,6 @@
if (secureRenegotiation) {
clientVerifyData = mesg.getVerifyData();
}
-
- /*
- * Update state machine so server MUST send 'finished' next.
- * (In "long" handshake case; in short case, we're responding
- * to its message.)
- */
- state = HandshakeMessage.ht_finished - 1;
}
@@ -1361,10 +1398,10 @@
// create the ClientHello message
ClientHello clientHelloMessage = new ClientHello(
sslContext.getSecureRandom(), maxProtocolVersion,
- sessionId, cipherSuites);
+ sessionId, cipherSuites, isDTLS);
// add signature_algorithm extension
- if (maxProtocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (maxProtocolVersion.useTLS12PlusSpec()) {
// we will always send the signature_algorithm extension
Collection<SignatureAndHashAlgorithm> localSignAlgs =
getLocalSupportedSignAlgs();
@@ -1389,6 +1426,37 @@
}
}
+ // add max_fragment_length extension
+ if (enableMFLExtension) {
+ if (session != null) {
+ // The same extension should be sent for resumption.
+ requestedMFLength = session.getNegotiatedMaxFragSize();
+ } else if (maximumPacketSize != 0) {
+ // Maybe we can calculate the fragment size more accurate
+ // by condering the enabled cipher suites in the future.
+ requestedMFLength = maximumPacketSize;
+ if (isDTLS) {
+ requestedMFLength -= DTLSRecord.maxPlaintextPlusSize;
+ } else {
+ requestedMFLength -= SSLRecord.maxPlaintextPlusSize;
+ }
+ } else {
+ // Need no max_fragment_length extension.
+ requestedMFLength = -1;
+ }
+
+ if ((requestedMFLength > 0) &&
+ MaxFragmentLengthExtension.needFragLenNego(requestedMFLength)) {
+
+ requestedMFLength =
+ MaxFragmentLengthExtension.getValidMaxFragLen(
+ requestedMFLength);
+ clientHelloMessage.addMFLExtension(requestedMFLength);
+ } else {
+ requestedMFLength = -1;
+ }
+ }
+
// reset the client random cookie
clnt_random = clientHelloMessage.clnt_random;
@@ -1403,6 +1471,11 @@
clientHelloMessage.addRenegotiationInfoExtension(clientVerifyData);
}
+ if (isDTLS) {
+ // Cookie exchange need to reserve the initial ClientHello message.
+ initialClientHelloMsg = clientHelloMessage;
+ }
+
return clientHelloMessage;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchange.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import javax.crypto.SecretKey;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.security.Principal;
+
+/**
+ * Models a non-certificate based ClientKeyExchange
+ */
+public abstract class ClientKeyExchange extends HandshakeMessage {
+
+ public ClientKeyExchange() {
+ }
+
+ @Override
+ int messageType() {
+ return ht_client_key_exchange;
+ }
+
+ @Override
+ abstract public int messageLength();
+
+ @Override
+ abstract public void send(HandshakeOutStream s) throws IOException;
+
+ @Override
+ abstract public void print(PrintStream s) throws IOException;
+
+ abstract public SecretKey clientKeyExchange();
+
+ abstract public Principal getPeerPrincipal();
+
+ abstract public Principal getLocalPrincipal();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchangeService.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import sun.security.action.GetPropertyAction;
+
+import java.io.File;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.security.SecureRandom;
+import java.util.*;
+
+/**
+ * Models a service that provides support for a particular client key exchange
+ * mode. Currently used to implement Kerberos-related cipher suites.
+ *
+ * @since 1.9
+ */
+public interface ClientKeyExchangeService {
+
+ static class Loader {
+ private static final Map<String,ClientKeyExchangeService>
+ providers = new HashMap<>();
+
+ static {
+ final String key = "java.home";
+ String path = AccessController.doPrivileged(
+ new GetPropertyAction(key), null,
+ new PropertyPermission(key, "read"));
+ ServiceLoader<ClientKeyExchangeService> sc =
+ AccessController.doPrivileged(
+ (PrivilegedAction<ServiceLoader<ClientKeyExchangeService>>)
+ () -> ServiceLoader.loadInstalled(ClientKeyExchangeService.class),
+ null,
+ new FilePermission(new File(path, "-").toString(), "read"));
+ Iterator<ClientKeyExchangeService> iter = sc.iterator();
+ while (iter.hasNext()) {
+ ClientKeyExchangeService cs = iter.next();
+ for (String ex: cs.supported()) {
+ providers.put(ex, cs);
+ }
+ }
+ }
+
+ }
+
+ public static ClientKeyExchangeService find(String ex) {
+ return Loader.providers.get(ex);
+ }
+
+
+ /**
+ * Returns the supported key exchange modes by this provider.
+ * @return the supported key exchange modes
+ */
+ String[] supported();
+
+ /**
+ * Returns a generalized credential object on the server side. The server
+ * side can use the info to determine if a cipher suite can be enabled.
+ * @param acc the AccessControlContext of the SSL session
+ * @return the credential object
+ */
+ Object getServiceCreds(AccessControlContext acc);
+
+ /**
+ * Returns the host name for a service principal. The info can be used in
+ * SNI or host name verifier.
+ * @param principal the principal of a service
+ * @return the string formed host name
+ */
+ String getServiceHostName(Principal principal);
+
+ /**
+ * Returns whether the specified principal is related to the current
+ * SSLSession. The info can be used to verify a SSL resume.
+ * @param isClient if true called from client side, otherwise from server
+ * @param acc the AccessControlContext of the SSL session
+ * @param p the specified principal
+ * @return true if related
+ */
+ boolean isRelated(boolean isClient, AccessControlContext acc, Principal p);
+
+ /**
+ * Creates the ClientKeyExchange object on the client side.
+ * @param serverName the intented peer name
+ * @param acc the AccessControlContext of the SSL session
+ * @param protocolVersion the TLS protocol version
+ * @param rand the SecureRandom that will used to generate the premaster
+ * @return the new Exchanger object
+ * @throws IOException if there is an error
+ */
+ ClientKeyExchange createClientExchange(String serverName, AccessControlContext acc,
+ ProtocolVersion protocolVersion, SecureRandom rand) throws IOException;
+
+ /**
+ * Create the ClientKeyExchange on the server side.
+ * @param protocolVersion the protocol version
+ * @param clientVersion the input protocol version
+ * @param rand a SecureRandom object used to generate premaster
+ * (if the server has to create one)
+ * @param encodedTicket the ticket from client
+ * @param encrypted the encrypted premaster secret from client
+ * @param acc the AccessControlContext of the SSL session
+ * @param ServiceCreds the service side credentials object as retrived from
+ * {@link #getServiceCreds}
+ * @return the new Exchanger object
+ * @throws IOException if there is an error
+ */
+ ClientKeyExchange createServerExchange(
+ ProtocolVersion protocolVersion, ProtocolVersion clientVersion,
+ SecureRandom rand, byte[] encodedTicket, byte[] encrypted,
+ AccessControlContext acc, Object ServiceCreds) throws IOException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,1265 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+import javax.crypto.BadPaddingException;
+
+import javax.net.ssl.*;
+
+import sun.misc.HexDumpEncoder;
+import static sun.security.ssl.HandshakeMessage.*;
+
+/**
+ * DTLS {@code InputRecord} implementation for {@code SSLEngine}.
+ */
+final class DTLSInputRecord extends InputRecord implements DTLSRecord {
+
+ private DTLSReassembler reassembler = null;
+
+ // Cache the session identifier for the detection of session-resuming
+ // handshake.
+ byte[] prevSessionID = new byte[0];
+
+ int readEpoch;
+
+ int prevReadEpoch;
+ Authenticator prevReadAuthenticator;
+ CipherBox prevReadCipher;
+
+ DTLSInputRecord() {
+ this.readEpoch = 0;
+ this.readAuthenticator = new MAC(true);
+
+ this.prevReadEpoch = 0;
+ this.prevReadCipher = CipherBox.NULL;
+ this.prevReadAuthenticator = new MAC(true);
+ }
+
+ @Override
+ void changeReadCiphers(Authenticator readAuthenticator,
+ CipherBox readCipher) {
+
+ prevReadCipher.dispose();
+
+ this.prevReadAuthenticator = this.readAuthenticator;
+ this.prevReadCipher = this.readCipher;
+ this.prevReadEpoch = this.readEpoch;
+
+ this.readAuthenticator = readAuthenticator;
+ this.readCipher = readCipher;
+ this.readEpoch++;
+ }
+
+ @Override
+ synchronized public void close() throws IOException {
+ if (!isClosed) {
+ prevReadCipher.dispose();
+ super.close();
+ }
+ }
+
+ @Override
+ boolean isEmpty() {
+ return ((reassembler == null) || reassembler.isEmpty());
+ }
+
+ @Override
+ int estimateFragmentSize(int packetSize) {
+ int macLen = 0;
+ if (readAuthenticator instanceof MAC) {
+ macLen = ((MAC)readAuthenticator).MAClen();
+ }
+
+ if (packetSize > 0) {
+ return readCipher.estimateFragmentSize(
+ packetSize, macLen, headerSize);
+ } else {
+ return Record.maxDataSize;
+ }
+ }
+
+ @Override
+ void expectingFinishFlight() {
+ if (reassembler != null) {
+ reassembler.expectingFinishFlight();
+ }
+ }
+
+ @Override
+ Plaintext acquirePlaintext() {
+ if (reassembler != null) {
+ Plaintext plaintext = reassembler.acquirePlaintext();
+ if (reassembler.finished()) {
+ // discard all buffered unused message.
+ reassembler = null;
+ }
+
+ return plaintext;
+ }
+
+ return null;
+ }
+
+ @Override
+ Plaintext decode(ByteBuffer packet) {
+
+ if (isClosed) {
+ return null;
+ }
+
+ if (debug != null && Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Raw read]: length = " + packet.remaining(), packet);
+ }
+
+ // The caller should have validated the record.
+ int srcPos = packet.position();
+ int srcLim = packet.limit();
+
+ byte contentType = packet.get(); // pos: 0
+ byte majorVersion = packet.get(); // pos: 1
+ byte minorVersion = packet.get(); // pos: 2
+ byte[] recordEnS = new byte[8]; // epoch + seqence
+ packet.get(recordEnS);
+ int recordEpoch = ((recordEnS[0] & 0xFF) << 8) |
+ (recordEnS[1] & 0xFF); // pos: 3, 4
+ long recordSeq = Authenticator.toLong(recordEnS);
+ int contentLen = ((packet.get() & 0xFF) << 8) |
+ (packet.get() & 0xFF); // pos: 11, 12
+
+ if (debug != null && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", READ: " +
+ ProtocolVersion.valueOf(majorVersion, minorVersion) +
+ " " + Record.contentName(contentType) + ", length = " +
+ contentLen);
+ }
+
+ int recLim = srcPos + DTLSRecord.headerSize + contentLen;
+ if (this.readEpoch > recordEpoch) {
+ // Discard old records delivered before this epoch.
+
+ // Reset the position of the packet buffer.
+ packet.position(recLim);
+ return null;
+ }
+
+ if (this.readEpoch < recordEpoch) {
+ if (contentType != Record.ct_handshake) {
+ // just discard it if not a handshake message
+ packet.position(recLim);
+ return null;
+ }
+
+ // Not ready to decrypt this record, may be encrypted Finished
+ // message, need to buffer it.
+ if (reassembler == null) {
+ reassembler = new DTLSReassembler();
+ }
+
+ byte[] fragment = new byte[contentLen];
+ packet.get(fragment); // copy the fragment
+ RecordFragment buffered = new RecordFragment(fragment, contentType,
+ majorVersion, minorVersion,
+ recordEnS, recordEpoch, recordSeq, true);
+
+ reassembler.queueUpFragment(buffered);
+
+ // consume the full record in the packet buffer.
+ packet.position(recLim);
+
+ Plaintext plaintext = reassembler.acquirePlaintext();
+ if (reassembler.finished()) {
+ // discard all buffered unused message.
+ reassembler = null;
+ }
+
+ return plaintext;
+ }
+
+ if (this.readEpoch == recordEpoch) {
+ // decrypt the fragment
+ packet.limit(recLim);
+ packet.position(srcPos + DTLSRecord.headerSize);
+
+ ByteBuffer plaintextFragment;
+ try {
+ plaintextFragment = decrypt(readAuthenticator,
+ readCipher, contentType, packet, recordEnS);
+ } catch (BadPaddingException bpe) {
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(Thread.currentThread().getName() +
+ " discard invalid record: " + bpe);
+ }
+
+ // invalid, discard this record [section 4.1.2.7, RFC 6347]
+ return null;
+ } finally {
+ // comsume a complete record
+ packet.limit(srcLim);
+ packet.position(recLim);
+ }
+
+ if (contentType != Record.ct_change_cipher_spec &&
+ contentType != Record.ct_handshake) { // app data or alert
+ // no retransmission
+ return new Plaintext(contentType, majorVersion, minorVersion,
+ recordEpoch, recordSeq, plaintextFragment);
+ }
+
+ if (contentType == Record.ct_change_cipher_spec) {
+ if (reassembler == null) {
+ // handshake has not started, should be an
+ // old handshake message, discard it.
+ return null;
+ }
+
+ reassembler.queueUpFragment(
+ new RecordFragment(plaintextFragment, contentType,
+ majorVersion, minorVersion,
+ recordEnS, recordEpoch, recordSeq, false));
+ } else { // handshake record
+ // One record may contain 1+ more handshake messages.
+ while (plaintextFragment.remaining() > 0) {
+
+ HandshakeFragment hsFrag = parseHandshakeMessage(
+ contentType, majorVersion, minorVersion,
+ recordEnS, recordEpoch, recordSeq, plaintextFragment);
+
+ if (hsFrag == null) {
+ // invalid, discard this record
+ return null;
+ }
+
+ if ((reassembler == null) &&
+ isKickstart(hsFrag.handshakeType)) {
+ reassembler = new DTLSReassembler();
+ }
+
+ if (reassembler != null) {
+ reassembler.queueUpHandshake(hsFrag);
+ } // else, just ignore the message.
+ }
+ }
+
+ // Completed the read of the full record. Acquire the reassembled
+ // messages.
+ if (reassembler != null) {
+ Plaintext plaintext = reassembler.acquirePlaintext();
+ if (reassembler.finished()) {
+ // discard all buffered unused message.
+ reassembler = null;
+ }
+
+ return plaintext;
+ }
+ }
+
+ return null; // make the complier happy
+ }
+
+ @Override
+ int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
+
+ // DTLS length field is in bytes 11/12
+ if (packet.remaining() < headerSize) {
+ return -1;
+ }
+
+ // Last sanity check that it's not a wild record
+ int pos = packet.position();
+
+ // Check the content type of the record.
+ byte contentType = packet.get(pos);
+ if (!Record.isValidContentType(contentType)) {
+ throw new SSLException(
+ "Unrecognized SSL message, plaintext connection?");
+ }
+
+ // Check the protocol version of the record.
+ ProtocolVersion recordVersion =
+ ProtocolVersion.valueOf(packet.get(pos + 1), packet.get(pos + 2));
+ checkRecordVersion(recordVersion, false);
+
+ // Get the fragment length of the record.
+ int fragLen = ((packet.get(pos + 11) & 0xFF) << 8) +
+ (packet.get(pos + 12) & 0xFF) + headerSize;
+ if (fragLen > Record.maxFragmentSize) {
+ throw new SSLException(
+ "Record overflow, fragment length (" + fragLen +
+ ") MUST not exceed " + Record.maxFragmentSize);
+ }
+
+ return fragLen;
+ }
+
+ @Override
+ void checkRecordVersion(ProtocolVersion recordVersion,
+ boolean allowSSL20Hello) throws SSLException {
+
+ if (!recordVersion.maybeDTLSProtocol()) {
+ throw new SSLException(
+ "Unrecognized record version " + recordVersion +
+ " , plaintext connection?");
+ }
+ }
+
+ private static boolean isKickstart(byte handshakeType) {
+ return (handshakeType == HandshakeMessage.ht_client_hello) ||
+ (handshakeType == HandshakeMessage.ht_hello_request) ||
+ (handshakeType == HandshakeMessage.ht_hello_verify_request);
+ }
+
+ private static HandshakeFragment parseHandshakeMessage(
+ byte contentType, byte majorVersion, byte minorVersion,
+ byte[] recordEnS, int recordEpoch, long recordSeq,
+ ByteBuffer plaintextFragment) {
+
+ int remaining = plaintextFragment.remaining();
+ if (remaining < handshakeHeaderSize) {
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(
+ Thread.currentThread().getName() +
+ " discard invalid record: " +
+ "too small record to hold a handshake fragment");
+ }
+
+ // invalid, discard this record [section 4.1.2.7, RFC 6347]
+ return null;
+ }
+
+ byte handshakeType = plaintextFragment.get(); // pos: 0
+ int messageLength =
+ ((plaintextFragment.get() & 0xFF) << 16) |
+ ((plaintextFragment.get() & 0xFF) << 8) |
+ (plaintextFragment.get() & 0xFF); // pos: 1-3
+ int messageSeq =
+ ((plaintextFragment.get() & 0xFF) << 8) |
+ (plaintextFragment.get() & 0xFF); // pos: 4/5
+ int fragmentOffset =
+ ((plaintextFragment.get() & 0xFF) << 16) |
+ ((plaintextFragment.get() & 0xFF) << 8) |
+ (plaintextFragment.get() & 0xFF); // pos: 6-8
+ int fragmentLength =
+ ((plaintextFragment.get() & 0xFF) << 16) |
+ ((plaintextFragment.get() & 0xFF) << 8) |
+ (plaintextFragment.get() & 0xFF); // pos: 9-11
+ if ((remaining - handshakeHeaderSize) < fragmentLength) {
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(
+ Thread.currentThread().getName() +
+ " discard invalid record: " +
+ "not a complete handshake fragment in the record");
+ }
+
+ // invalid, discard this record [section 4.1.2.7, RFC 6347]
+ return null;
+ }
+
+ byte[] fragment = new byte[fragmentLength];
+ plaintextFragment.get(fragment);
+
+ return new HandshakeFragment(fragment, contentType,
+ majorVersion, minorVersion,
+ recordEnS, recordEpoch, recordSeq,
+ handshakeType, messageLength,
+ messageSeq, fragmentOffset, fragmentLength);
+ }
+
+ // buffered record fragment
+ private static class RecordFragment implements Comparable<RecordFragment> {
+ boolean isCiphertext;
+
+ byte contentType;
+ byte majorVersion;
+ byte minorVersion;
+ int recordEpoch;
+ long recordSeq;
+ byte[] recordEnS;
+ byte[] fragment;
+
+ RecordFragment(ByteBuffer fragBuf, byte contentType,
+ byte majorVersion, byte minorVersion, byte[] recordEnS,
+ int recordEpoch, long recordSeq, boolean isCiphertext) {
+ this((byte[])null, contentType, majorVersion, minorVersion,
+ recordEnS, recordEpoch, recordSeq, isCiphertext);
+
+ this.fragment = new byte[fragBuf.remaining()];
+ fragBuf.get(this.fragment);
+ }
+
+ RecordFragment(byte[] fragment, byte contentType,
+ byte majorVersion, byte minorVersion, byte[] recordEnS,
+ int recordEpoch, long recordSeq, boolean isCiphertext) {
+ this.isCiphertext = isCiphertext;
+
+ this.contentType = contentType;
+ this.majorVersion = majorVersion;
+ this.minorVersion = minorVersion;
+ this.recordEpoch = recordEpoch;
+ this.recordSeq = recordSeq;
+ this.recordEnS = recordEnS;
+ this.fragment = fragment; // The caller should have cloned
+ // the buffer if necessary.
+ }
+
+ @Override
+ public int compareTo(RecordFragment o) {
+ return Long.compareUnsigned(this.recordSeq, o.recordSeq);
+ }
+ }
+
+ // buffered handshake message
+ private static final class HandshakeFragment extends RecordFragment {
+
+ byte handshakeType; // handshake msg_type
+ int messageSeq; // message_seq
+ int messageLength; // Handshake body length
+ int fragmentOffset; // fragment_offset
+ int fragmentLength; // fragment_length
+
+ HandshakeFragment(byte[] fragment, byte contentType,
+ byte majorVersion, byte minorVersion, byte[] recordEnS,
+ int recordEpoch, long recordSeq,
+ byte handshakeType, int messageLength,
+ int messageSeq, int fragmentOffset, int fragmentLength) {
+
+ super(fragment, contentType, majorVersion, minorVersion,
+ recordEnS, recordEpoch , recordSeq, false);
+
+ this.handshakeType = handshakeType;
+ this.messageSeq = messageSeq;
+ this.messageLength = messageLength;
+ this.fragmentOffset = fragmentOffset;
+ this.fragmentLength = fragmentLength;
+ }
+
+ @Override
+ public int compareTo(RecordFragment o) {
+ if (o instanceof HandshakeFragment) {
+ HandshakeFragment other = (HandshakeFragment)o;
+ if (this.messageSeq != other.messageSeq) {
+ // keep the insertion order for the same message
+ return this.messageSeq - other.messageSeq;
+ }
+ }
+
+ return Long.compareUnsigned(this.recordSeq, o.recordSeq);
+ }
+ }
+
+ private static final class HoleDescriptor {
+ int offset; // fragment_offset
+ int limit; // fragment_offset + fragment_length
+
+ HoleDescriptor(int offset, int limit) {
+ this.offset = offset;
+ this.limit = limit;
+ }
+ }
+
+ final class DTLSReassembler {
+ TreeSet<RecordFragment> bufferedFragments = new TreeSet<>();
+
+ HashMap<Byte, List<HoleDescriptor>> holesMap = new HashMap<>(5);
+
+ // Epoch, sequence number and handshake message sequence of the
+ // beginning message of a flight.
+ byte flightType = (byte)0xFF;
+
+ int flightTopEpoch = 0;
+ long flightTopRecordSeq = -1;
+ int flightTopMessageSeq = 0;
+
+ // Epoch, sequence number and handshake message sequence of the
+ // next message acquisition of a flight.
+ int nextRecordEpoch = 0; // next record epoch
+ long nextRecordSeq = 0; // next record sequence number
+ int nextMessageSeq = 0; // next handshake message number
+
+ // Expect ChangeCipherSpec and Finished messages for the final flight.
+ boolean expectCCSFlight = false;
+
+ // Ready to process this flight if received all messages of the flight.
+ boolean flightIsReady = false;
+ boolean needToCheckFlight = false;
+
+ // Is it a session-resuming abbreviated handshake.?
+ boolean isAbbreviatedHandshake = false;
+
+ // The handshke fragment with the biggest record sequence number
+ // in a flight, not counting the Finished message.
+ HandshakeFragment lastHandshakeFragment = null;
+
+ // Is handshake (intput) finished?
+ boolean handshakeFinished = false;
+
+ DTLSReassembler() {
+ // blank
+ }
+
+ boolean finished() {
+ return handshakeFinished;
+ }
+
+ void expectingFinishFlight() {
+ expectCCSFlight = true;
+ }
+
+ void queueUpHandshake(HandshakeFragment hsf) {
+
+ if ((nextRecordEpoch > hsf.recordEpoch) ||
+ (nextRecordSeq > hsf.recordSeq) ||
+ (nextMessageSeq > hsf.messageSeq)) {
+ // too old, discard this record
+ return;
+ }
+
+ // Is it the first message of next flight?
+ if ((flightTopMessageSeq == hsf.messageSeq) &&
+ (hsf.fragmentOffset == 0) && (flightTopRecordSeq == -1)) {
+
+ flightType = hsf.handshakeType;
+ flightTopEpoch = hsf.recordEpoch;
+ flightTopRecordSeq = hsf.recordSeq;
+
+ if (hsf.handshakeType == HandshakeMessage.ht_server_hello) {
+ // Is it a session-resuming handshake?
+ try {
+ isAbbreviatedHandshake =
+ isSessionResuming(hsf.fragment, prevSessionID);
+ } catch (SSLException ssle) {
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(
+ Thread.currentThread().getName() +
+ " discard invalid record: " + ssle);
+ }
+
+ // invalid, discard it [section 4.1.2.7, RFC 6347]
+ return;
+ }
+
+ if (!isAbbreviatedHandshake) {
+ prevSessionID = getSessionID(hsf.fragment);
+ }
+ }
+ }
+
+ boolean fragmented = false;
+ if ((hsf.fragmentOffset) != 0 ||
+ (hsf.fragmentLength != hsf.messageLength)) {
+
+ fragmented = true;
+ }
+
+ List<HoleDescriptor> holes = holesMap.get(hsf.handshakeType);
+ if (holes == null) {
+ if (!fragmented) {
+ holes = Collections.emptyList();
+ } else {
+ holes = new LinkedList<HoleDescriptor>();
+ holes.add(new HoleDescriptor(0, hsf.messageLength));
+ }
+ holesMap.put(hsf.handshakeType, holes);
+ } else if (holes.isEmpty()) {
+ // Have got the full handshake message. This record may be
+ // a handshake message retransmission. Discard this record.
+ //
+ // It's OK to discard retransmission as the handshake hash
+ // is computed as if each handshake message had been sent
+ // as a single fragment.
+ //
+ // Note that ClientHello messages are delivered twice in
+ // DTLS handshaking.
+ if ((hsf.handshakeType != HandshakeMessage.ht_client_hello &&
+ hsf.handshakeType != ht_hello_verify_request) ||
+ (nextMessageSeq != hsf.messageSeq)) {
+ return;
+ }
+
+ if (fragmented) {
+ holes = new LinkedList<HoleDescriptor>();
+ holes.add(new HoleDescriptor(0, hsf.messageLength));
+ }
+ holesMap.put(hsf.handshakeType, holes);
+ }
+
+ if (fragmented) {
+ int fragmentLimit = hsf.fragmentOffset + hsf.fragmentLength;
+ for (int i = 0; i < holes.size(); i++) {
+
+ HoleDescriptor hole = holes.get(i);
+ if ((hole.limit <= hsf.fragmentOffset) ||
+ (hole.offset >= fragmentLimit)) {
+ // Also discard overlapping handshake retransmissions.
+ continue;
+ }
+
+ // The ranges SHOULD NOT overlap.
+ if (((hole.offset > hsf.fragmentOffset) &&
+ (hole.offset < fragmentLimit)) ||
+ ((hole.limit > hsf.fragmentOffset) &&
+ (hole.limit < fragmentLimit))) {
+
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(
+ Thread.currentThread().getName() +
+ " discard invalid record: " +
+ "handshake fragment ranges are overlapping");
+ }
+
+ // invalid, discard it [section 4.1.2.7, RFC 6347]
+ return;
+ }
+
+ // This record interacts with this hole, fill the hole.
+ holes.remove(i);
+ // i--;
+
+ if (hsf.fragmentOffset > hole.offset) {
+ holes.add(new HoleDescriptor(
+ hole.offset, hsf.fragmentOffset));
+ // i++;
+ }
+
+ if (fragmentLimit < hole.limit) {
+ holes.add(new HoleDescriptor(
+ fragmentLimit, hole.limit));
+ // i++;
+ }
+
+ // As no ranges overlap, no interact with other holes.
+ break;
+ }
+ }
+
+ // append this fragment
+ bufferedFragments.add(hsf);
+
+ if ((lastHandshakeFragment == null) ||
+ (lastHandshakeFragment.compareTo(hsf) < 0)) {
+
+ lastHandshakeFragment = hsf;
+ }
+
+ if (flightIsReady) {
+ flightIsReady = false;
+ }
+ needToCheckFlight = true;
+ }
+
+ // queue up change_cipher_spec or encrypted message
+ void queueUpFragment(RecordFragment rf) {
+ if ((nextRecordEpoch > rf.recordEpoch) ||
+ (nextRecordSeq > rf.recordSeq)) {
+ // too old, discard this record
+ return;
+ }
+
+ // Is it the first message of next flight?
+ if (expectCCSFlight &&
+ (rf.contentType == Record.ct_change_cipher_spec)) {
+
+ flightType = (byte)0xFE;
+ flightTopEpoch = rf.recordEpoch;
+ flightTopRecordSeq = rf.recordSeq;
+ }
+
+ // append this fragment
+ bufferedFragments.add(rf);
+
+ if (flightIsReady) {
+ flightIsReady = false;
+ }
+ needToCheckFlight = true;
+ }
+
+ boolean isEmpty() {
+ return (bufferedFragments.isEmpty() ||
+ (!flightIsReady && !needToCheckFlight) ||
+ (needToCheckFlight && !flightIsReady()));
+ }
+
+ Plaintext acquirePlaintext() {
+ if (bufferedFragments.isEmpty()) {
+ // reset the flight
+ if (flightIsReady) {
+ flightIsReady = false;
+ needToCheckFlight = false;
+ }
+
+ return null;
+ }
+
+ if (!flightIsReady && needToCheckFlight) {
+ // check the fligth status
+ flightIsReady = flightIsReady();
+
+ // set for next flight
+ if (flightIsReady) {
+ flightTopMessageSeq = lastHandshakeFragment.messageSeq + 1;
+ flightTopRecordSeq = -1;
+ }
+
+ needToCheckFlight = false;
+ }
+
+ if (!flightIsReady) {
+ return null;
+ }
+
+ RecordFragment rFrag = bufferedFragments.first();
+ if (!rFrag.isCiphertext) {
+ // handshake message, or ChangeCipherSpec message
+ return acquireHandshakeMessage();
+ } else {
+ // a Finished message or other ciphertexts
+ return acquireCachedMessage();
+ }
+ }
+
+ private Plaintext acquireCachedMessage() {
+
+ RecordFragment rFrag = bufferedFragments.first();
+ if (readEpoch != rFrag.recordEpoch) {
+ if (readEpoch > rFrag.recordEpoch) {
+ // discard old records
+ bufferedFragments.remove(rFrag); // popup the fragment
+ }
+
+ // reset the flight
+ if (flightIsReady) {
+ flightIsReady = false;
+ }
+ return null;
+ }
+
+ bufferedFragments.remove(rFrag); // popup the fragment
+
+ ByteBuffer fragment = ByteBuffer.wrap(rFrag.fragment);
+ ByteBuffer plaintextFragment = null;
+ try {
+ plaintextFragment = decrypt(readAuthenticator, readCipher,
+ rFrag.contentType, fragment, rFrag.recordEnS);
+ } catch (BadPaddingException bpe) {
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(Thread.currentThread().getName() +
+ " discard invalid record: " + bpe);
+ }
+
+ // invalid, discard this record [section 4.1.2.7, RFC 6347]
+ return null;
+ }
+
+ // The ciphtext handshake message can only be Finished (the
+ // end of this flight), ClinetHello or HelloRequest (the
+ // beginning of the next flight) message. Need not to check
+ // any ChangeCipherSpec message.
+ if (rFrag.contentType == Record.ct_handshake) {
+ HandshakeFragment finFrag = null;
+ while (plaintextFragment.remaining() > 0) {
+ HandshakeFragment hsFrag = parseHandshakeMessage(
+ rFrag.contentType,
+ rFrag.majorVersion, rFrag.minorVersion,
+ rFrag.recordEnS, rFrag.recordEpoch, rFrag.recordSeq,
+ plaintextFragment);
+
+ if (hsFrag == null) {
+ // invalid, discard this record
+ return null;
+ }
+
+ if (hsFrag.handshakeType == HandshakeMessage.ht_finished) {
+ finFrag = hsFrag;
+
+ // reset for the next flight
+ this.flightType = (byte)0xFF;
+ this.flightTopEpoch = rFrag.recordEpoch;
+ this.flightTopMessageSeq = hsFrag.messageSeq + 1;
+ this.flightTopRecordSeq = -1;
+ } else {
+ // reset the flight
+ if (flightIsReady) {
+ flightIsReady = false;
+ }
+ queueUpHandshake(hsFrag);
+ }
+ }
+
+ this.nextRecordSeq = rFrag.recordSeq + 1;
+ this.nextMessageSeq = 0;
+
+ if (finFrag != null) {
+ this.nextRecordEpoch = finFrag.recordEpoch;
+ this.nextRecordSeq = finFrag.recordSeq + 1;
+ this.nextMessageSeq = finFrag.messageSeq + 1;
+
+ // Finished message does not fragment.
+ byte[] recordFrag = new byte[finFrag.messageLength + 4];
+ Plaintext plaintext = new Plaintext(finFrag.contentType,
+ finFrag.majorVersion, finFrag.minorVersion,
+ finFrag.recordEpoch, finFrag.recordSeq,
+ ByteBuffer.wrap(recordFrag));
+
+ // fill the handshake fragment of the record
+ recordFrag[0] = finFrag.handshakeType;
+ recordFrag[1] =
+ (byte)((finFrag.messageLength >>> 16) & 0xFF);
+ recordFrag[2] =
+ (byte)((finFrag.messageLength >>> 8) & 0xFF);
+ recordFrag[3] = (byte)(finFrag.messageLength & 0xFF);
+
+ System.arraycopy(finFrag.fragment, 0,
+ recordFrag, 4, finFrag.fragmentLength);
+
+ // handshake hashing
+ handshakeHashing(finFrag, plaintext);
+
+ // input handshake finished
+ handshakeFinished = true;
+
+ return plaintext;
+ } else {
+ return acquirePlaintext();
+ }
+ } else {
+ return new Plaintext(rFrag.contentType,
+ rFrag.majorVersion, rFrag.minorVersion,
+ rFrag.recordEpoch, rFrag.recordSeq,
+ plaintextFragment);
+ }
+ }
+
+ private Plaintext acquireHandshakeMessage() {
+
+ RecordFragment rFrag = bufferedFragments.first();
+ if (rFrag.contentType == Record.ct_change_cipher_spec) {
+ this.nextRecordEpoch = rFrag.recordEpoch + 1;
+ this.nextRecordSeq = 0;
+ // no change on next handshake message sequence number
+
+ bufferedFragments.remove(rFrag); // popup the fragment
+
+ // Reload if this message has been reserved for handshake hash.
+ handshakeHash.reload();
+
+ return new Plaintext(rFrag.contentType,
+ rFrag.majorVersion, rFrag.minorVersion,
+ rFrag.recordEpoch, rFrag.recordSeq,
+ ByteBuffer.wrap(rFrag.fragment));
+ } else { // rFrag.contentType == Record.ct_handshake
+ HandshakeFragment hsFrag = (HandshakeFragment)rFrag;
+ if ((hsFrag.messageLength == hsFrag.fragmentLength) &&
+ (hsFrag.fragmentOffset == 0)) { // no fragmentation
+
+ bufferedFragments.remove(rFrag); // popup the fragment
+
+ // this.nextRecordEpoch = hsFrag.recordEpoch;
+ this.nextRecordSeq = hsFrag.recordSeq + 1;
+ this.nextMessageSeq = hsFrag.messageSeq + 1;
+
+ // Note: may try to avoid byte array copy in the future.
+ byte[] recordFrag = new byte[hsFrag.messageLength + 4];
+ Plaintext plaintext = new Plaintext(hsFrag.contentType,
+ hsFrag.majorVersion, hsFrag.minorVersion,
+ hsFrag.recordEpoch, hsFrag.recordSeq,
+ ByteBuffer.wrap(recordFrag));
+
+ // fill the handshake fragment of the record
+ recordFrag[0] = hsFrag.handshakeType;
+ recordFrag[1] =
+ (byte)((hsFrag.messageLength >>> 16) & 0xFF);
+ recordFrag[2] =
+ (byte)((hsFrag.messageLength >>> 8) & 0xFF);
+ recordFrag[3] = (byte)(hsFrag.messageLength & 0xFF);
+
+ System.arraycopy(hsFrag.fragment, 0,
+ recordFrag, 4, hsFrag.fragmentLength);
+
+ // handshake hashing
+ handshakeHashing(hsFrag, plaintext);
+
+ return plaintext;
+ } else { // fragmented handshake message
+ // the first record
+ //
+ // Note: may try to avoid byte array copy in the future.
+ byte[] recordFrag = new byte[hsFrag.messageLength + 4];
+ Plaintext plaintext = new Plaintext(hsFrag.contentType,
+ hsFrag.majorVersion, hsFrag.minorVersion,
+ hsFrag.recordEpoch, hsFrag.recordSeq,
+ ByteBuffer.wrap(recordFrag));
+
+ // fill the handshake fragment of the record
+ recordFrag[0] = hsFrag.handshakeType;
+ recordFrag[1] =
+ (byte)((hsFrag.messageLength >>> 16) & 0xFF);
+ recordFrag[2] =
+ (byte)((hsFrag.messageLength >>> 8) & 0xFF);
+ recordFrag[3] = (byte)(hsFrag.messageLength & 0xFF);
+
+ int msgSeq = hsFrag.messageSeq;
+ long maxRecodeSN = hsFrag.recordSeq;
+ HandshakeFragment hmFrag = hsFrag;
+ do {
+ System.arraycopy(hmFrag.fragment, 0,
+ recordFrag, hmFrag.fragmentOffset + 4,
+ hmFrag.fragmentLength);
+ // popup the fragment
+ bufferedFragments.remove(rFrag);
+
+ if (maxRecodeSN < hmFrag.recordSeq) {
+ maxRecodeSN = hmFrag.recordSeq;
+ }
+
+ // Note: may buffer retransmitted fragments in order to
+ // speed up the reassembly in the future.
+
+ // read the next buffered record
+ if (!bufferedFragments.isEmpty()) {
+ rFrag = bufferedFragments.first();
+ if (rFrag.contentType != Record.ct_handshake) {
+ break;
+ } else {
+ hmFrag = (HandshakeFragment)rFrag;
+ }
+ }
+ } while (!bufferedFragments.isEmpty() &&
+ (msgSeq == hmFrag.messageSeq));
+
+ // handshake hashing
+ handshakeHashing(hsFrag, plaintext);
+
+ this.nextRecordSeq = maxRecodeSN + 1;
+ this.nextMessageSeq = msgSeq + 1;
+
+ return plaintext;
+ }
+ }
+ }
+
+ boolean flightIsReady() {
+
+ //
+ // the ChangeCipherSpec/Finished flight
+ //
+ if (expectCCSFlight) {
+ // Have the ChangeCipherSpec/Finished messages been received?
+ return hasFinisedMessage(bufferedFragments);
+ }
+
+ if (flightType == (byte)0xFF) {
+ return false;
+ }
+
+ if ((flightType == HandshakeMessage.ht_client_hello) ||
+ (flightType == HandshakeMessage.ht_hello_request) ||
+ (flightType == HandshakeMessage.ht_hello_verify_request)) {
+
+ // single handshake message flight
+ return hasCompleted(holesMap.get(flightType));
+ }
+
+ //
+ // the ServerHello flight
+ //
+ if (flightType == HandshakeMessage.ht_server_hello) {
+ // Firstly, check the first flight handshake message.
+ if (!hasCompleted(holesMap.get(flightType))) {
+ return false;
+ }
+
+ //
+ // an abbreviated handshake
+ //
+ if (isAbbreviatedHandshake) {
+ // Ready to use the flight if received the
+ // ChangeCipherSpec and Finished messages.
+ return hasFinisedMessage(bufferedFragments);
+ }
+
+ //
+ // a full handshake
+ //
+ if (lastHandshakeFragment.handshakeType !=
+ HandshakeMessage.ht_server_hello_done) {
+ // Not yet got the final message of the flight.
+ return false;
+ }
+
+ // Have all handshake message been received?
+ return hasCompleted(bufferedFragments,
+ flightTopMessageSeq, lastHandshakeFragment.messageSeq);
+ }
+
+ //
+ // the ClientKeyExchange flight
+ //
+ // Note: need to consider more messages in this flight if
+ // ht_supplemental_data and ht_certificate_url are
+ // suppported in the future.
+ //
+ if ((flightType == HandshakeMessage.ht_certificate) ||
+ (flightType == HandshakeMessage.ht_client_key_exchange)) {
+
+ // Firstly, check the first flight handshake message.
+ if (!hasCompleted(holesMap.get(flightType))) {
+ return false;
+ }
+
+ if (!hasFinisedMessage(bufferedFragments)) {
+ // not yet got the ChangeCipherSpec/Finished messages
+ return false;
+ }
+
+ if (flightType == HandshakeMessage.ht_client_key_exchange) {
+ // single handshake message flight
+ return true;
+ }
+
+ //
+ // flightType == HandshakeMessage.ht_certificate
+ //
+ // We don't support certificates containing fixed
+ // Diffie-Hellman parameters. Therefore, CertificateVerify
+ // message is required if client Certificate message presents.
+ //
+ if (lastHandshakeFragment.handshakeType !=
+ HandshakeMessage.ht_certificate_verify) {
+ // Not yet got the final message of the flight.
+ return false;
+ }
+
+ // Have all handshake message been received?
+ return hasCompleted(bufferedFragments,
+ flightTopMessageSeq, lastHandshakeFragment.messageSeq);
+ }
+
+ //
+ // Otherwise, need to receive more handshake messages.
+ //
+ return false;
+ }
+
+ private boolean isSessionResuming(
+ byte[] fragment, byte[] prevSid) throws SSLException {
+
+ // As the first fragment of ServerHello should be big enough
+ // to hold the session_id field, need not to worry about the
+ // fragmentation here.
+ if ((fragment == null) || (fragment.length < 38)) {
+ // 38: the minimal ServerHello body length
+ throw new SSLException(
+ "Invalid ServerHello message: no sufficient data");
+ }
+
+ int sidLen = fragment[34]; // 34: the length field
+ if (sidLen > 32) { // opaque SessionID<0..32>
+ throw new SSLException(
+ "Invalid ServerHello message: invalid session id");
+ }
+
+ if (fragment.length < 38 + sidLen) {
+ throw new SSLException(
+ "Invalid ServerHello message: no sufficient data");
+ }
+
+ if (sidLen != 0 && (prevSid.length == sidLen)) {
+ // may be a session-resuming handshake
+ for (int i = 0; i < sidLen; i++) {
+ if (prevSid[i] != fragment[35 + i]) {
+ // 35: the session identifier
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private byte[] getSessionID(byte[] fragment) {
+ // The validity has been checked in the call to isSessionResuming().
+ int sidLen = fragment[34]; // 34: the sessionID length field
+
+ byte[] temporary = new byte[sidLen];
+ System.arraycopy(fragment, 35, temporary, 0, sidLen);
+
+ return temporary;
+ }
+
+ // Looking for the ChangeCipherSpec and Finished messages.
+ //
+ // As the cached Finished message should be a ciphertext, we don't
+ // exactly know a ciphertext is a Finished message or not. According
+ // to the spec of TLS/DTLS handshaking, a Finished message is always
+ // sent immediately after a ChangeCipherSpec message. The first
+ // ciphertext handshake message should be the expected Finished message.
+ private boolean hasFinisedMessage(
+ Set<RecordFragment> fragments) {
+
+ boolean hasCCS = false;
+ boolean hasFin = false;
+ for (RecordFragment fragment : fragments) {
+ if (fragment.contentType == Record.ct_change_cipher_spec) {
+ if (hasFin) {
+ return true;
+ }
+ hasCCS = true;
+ } else if (fragment.contentType == Record.ct_handshake) {
+ // Finished is the first expected message of a new epoch.
+ if (fragment.isCiphertext) {
+ if (hasCCS) {
+ return true;
+ }
+ hasFin = true;
+ }
+ }
+ }
+
+ return hasFin && hasCCS;
+ }
+
+ private boolean hasCompleted(List<HoleDescriptor> holes) {
+ if (holes == null) {
+ // not yet received this kind of handshake message
+ return false;
+ }
+
+ return holes.isEmpty(); // no fragment hole for complete message
+ }
+
+ private boolean hasCompleted(
+ Set<RecordFragment> fragments,
+ int presentMsgSeq, int endMsgSeq) {
+
+ // The caller should have checked the completion of the first
+ // present handshake message. Need not to check it again.
+ for (RecordFragment rFrag : fragments) {
+ if ((rFrag.contentType != Record.ct_handshake) ||
+ rFrag.isCiphertext) {
+ break;
+ }
+
+ HandshakeFragment hsFrag = (HandshakeFragment)rFrag;
+ if (hsFrag.messageSeq == presentMsgSeq) {
+ continue;
+ } else if (hsFrag.messageSeq == (presentMsgSeq + 1)) {
+ // check the completion of the handshake message
+ if (!hasCompleted(holesMap.get(hsFrag.handshakeType))) {
+ return false;
+ }
+
+ presentMsgSeq = hsFrag.messageSeq;
+ } else {
+ // not yet got handshake message next to presentMsgSeq
+ break;
+ }
+ }
+
+ return (presentMsgSeq >= endMsgSeq);
+ // false: if not yet got all messages of the flight.
+ }
+
+ private void handshakeHashing(
+ HandshakeFragment hsFrag, Plaintext plaintext) {
+
+ byte hsType = hsFrag.handshakeType;
+ if ((hsType == HandshakeMessage.ht_hello_request) ||
+ (hsType == HandshakeMessage.ht_hello_verify_request)) {
+
+ // omitted from handshake hash computation
+ return;
+ }
+
+ if ((hsFrag.messageSeq == 0) &&
+ (hsType == HandshakeMessage.ht_client_hello)) {
+
+ // omit initial ClientHello message
+ //
+ // 4: handshake header
+ // 2: ClientHello.client_version
+ // 32: ClientHello.random
+ int sidLen = plaintext.fragment.get(38);
+
+ if (sidLen == 0) { // empty session_id, initial handshake
+ return;
+ }
+ }
+
+ // calculate the DTLS header
+ byte[] temporary = new byte[12]; // 12: handshake header size
+
+ // Handshake.msg_type
+ temporary[0] = hsFrag.handshakeType;
+
+ // Handshake.length
+ temporary[1] = (byte)((hsFrag.messageLength >> 16) & 0xFF);
+ temporary[2] = (byte)((hsFrag.messageLength >> 8) & 0xFF);
+ temporary[3] = (byte)(hsFrag.messageLength & 0xFF);
+
+ // Handshake.message_seq
+ temporary[4] = (byte)((hsFrag.messageSeq >> 8) & 0xFF);
+ temporary[5] = (byte)(hsFrag.messageSeq & 0xFF);
+
+ // Handshake.fragment_offset
+ temporary[6] = 0;
+ temporary[7] = 0;
+ temporary[8] = 0;
+
+ // Handshake.fragment_length
+ temporary[9] = temporary[1];
+ temporary[10] = temporary[2];
+ temporary[11] = temporary[3];
+
+ plaintext.fragment.position(4); // ignore the TLS header
+ if ((hsType != HandshakeMessage.ht_finished) &&
+ (hsType != HandshakeMessage.ht_certificate_verify)) {
+
+ if (handshakeHash == null) {
+ // used for cache only
+ handshakeHash = new HandshakeHash(false);
+ }
+ handshakeHash.update(temporary, 0, 12);
+ handshakeHash.update(plaintext.fragment);
+ } else {
+ // Reserve until this handshake message has been processed.
+ if (handshakeHash == null) {
+ // used for cache only
+ handshakeHash = new HandshakeHash(false);
+ }
+ handshakeHash.reserve(temporary, 0, 12);
+ handshakeHash.reserve(plaintext.fragment);
+ }
+ plaintext.fragment.position(0); // restore the position
+ }
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,597 @@
+/*
+ * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+
+import javax.crypto.BadPaddingException;
+
+import javax.net.ssl.*;
+
+import sun.misc.HexDumpEncoder;
+import static sun.security.ssl.Ciphertext.RecordType;
+
+/**
+ * DTLS {@code OutputRecord} implementation for {@code SSLEngine}.
+ */
+final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
+
+ private DTLSFragmenter fragmenter = null;
+
+ int writeEpoch;
+
+ int prevWriteEpoch;
+ Authenticator prevWriteAuthenticator;
+ CipherBox prevWriteCipher;
+
+ private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
+
+ DTLSOutputRecord() {
+ this.writeAuthenticator = new MAC(true);
+
+ this.writeEpoch = 0;
+ this.prevWriteEpoch = 0;
+ this.prevWriteCipher = CipherBox.NULL;
+ this.prevWriteAuthenticator = new MAC(true);
+
+ this.packetSize = DTLSRecord.maxRecordSize;
+ this.protocolVersion = ProtocolVersion.DEFAULT_DTLS;
+ }
+
+ @Override
+ void changeWriteCiphers(Authenticator writeAuthenticator,
+ CipherBox writeCipher) throws IOException {
+
+ encodeChangeCipherSpec();
+
+ prevWriteCipher.dispose();
+
+ this.prevWriteAuthenticator = this.writeAuthenticator;
+ this.prevWriteCipher = this.writeCipher;
+ this.prevWriteEpoch = this.writeEpoch;
+
+ this.writeAuthenticator = writeAuthenticator;
+ this.writeCipher = writeCipher;
+ this.writeEpoch++;
+
+ this.isFirstAppOutputRecord = true;
+
+ // set the epoch number
+ this.writeAuthenticator.setEpochNumber(this.writeEpoch);
+ }
+
+ @Override
+ void encodeAlert(byte level, byte description) throws IOException {
+ RecordMemo memo = new RecordMemo();
+
+ memo.contentType = Record.ct_alert;
+ memo.majorVersion = protocolVersion.major;
+ memo.minorVersion = protocolVersion.minor;
+ memo.encodeEpoch = writeEpoch;
+ memo.encodeCipher = writeCipher;
+ memo.encodeAuthenticator = writeAuthenticator;
+
+ memo.fragment = new byte[2];
+ memo.fragment[0] = level;
+ memo.fragment[1] = description;
+
+ alertMemos.add(memo);
+ }
+
+ @Override
+ void encodeChangeCipherSpec() throws IOException {
+ if (fragmenter == null) {
+ fragmenter = new DTLSFragmenter();
+ }
+ fragmenter.queueUpChangeCipherSpec();
+ }
+
+ @Override
+ void encodeHandshake(byte[] source,
+ int offset, int length) throws IOException {
+
+ if (firstMessage) {
+ firstMessage = false;
+ }
+
+ if (fragmenter == null) {
+ fragmenter = new DTLSFragmenter();
+ }
+
+ fragmenter.queueUpHandshake(source, offset, length);
+ }
+
+ @Override
+ Ciphertext encode(ByteBuffer[] sources, int offset, int length,
+ ByteBuffer destination) throws IOException {
+
+ if (writeAuthenticator.seqNumOverflow()) {
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", sequence number extremely close to overflow " +
+ "(2^64-1 packets). Closing connection.");
+ }
+
+ throw new SSLHandshakeException("sequence number overflow");
+ }
+
+ // not apply to handshake message
+ int macLen = 0;
+ if (writeAuthenticator instanceof MAC) {
+ macLen = ((MAC)writeAuthenticator).MAClen();
+ }
+
+ int fragLen;
+ if (packetSize > 0) {
+ fragLen = Math.min(maxRecordSize, packetSize);
+ fragLen = writeCipher.calculateFragmentSize(
+ fragLen, macLen, headerSize);
+
+ fragLen = Math.min(fragLen, Record.maxDataSize);
+ } else {
+ fragLen = Record.maxDataSize;
+ }
+
+ if (fragmentSize > 0) {
+ fragLen = Math.min(fragLen, fragmentSize);
+ }
+
+ int dstPos = destination.position();
+ int dstLim = destination.limit();
+ int dstContent = dstPos + headerSize +
+ writeCipher.getExplicitNonceSize();
+ destination.position(dstContent);
+
+ int remains = Math.min(fragLen, destination.remaining());
+ fragLen = 0;
+ int srcsLen = offset + length;
+ for (int i = offset; (i < srcsLen) && (remains > 0); i++) {
+ int amount = Math.min(sources[i].remaining(), remains);
+ int srcLimit = sources[i].limit();
+ sources[i].limit(sources[i].position() + amount);
+ destination.put(sources[i]);
+ sources[i].limit(srcLimit); // restore the limit
+ remains -= amount;
+ fragLen += amount;
+ }
+
+ destination.limit(destination.position());
+ destination.position(dstContent);
+
+ if ((debug != null) && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", WRITE: " + protocolVersion + " " +
+ Record.contentName(Record.ct_application_data) +
+ ", length = " + destination.remaining());
+ }
+
+ // Encrypt the fragment and wrap up a record.
+ long recordSN = encrypt(writeAuthenticator, writeCipher,
+ Record.ct_application_data, destination,
+ dstPos, dstLim, headerSize,
+ protocolVersion, true);
+
+ if ((debug != null) && Debug.isOn("packet")) {
+ ByteBuffer temporary = destination.duplicate();
+ temporary.limit(temporary.position());
+ temporary.position(dstPos);
+ Debug.printHex(
+ "[Raw write]: length = " + temporary.remaining(),
+ temporary);
+ }
+
+ // remain the limit unchanged
+ destination.limit(dstLim);
+
+ return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
+ }
+
+ @Override
+ Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
+ if (alertMemos != null && !alertMemos.isEmpty()) {
+ RecordMemo memo = alertMemos.pop();
+
+ int macLen = 0;
+ if (memo.encodeAuthenticator instanceof MAC) {
+ macLen = ((MAC)memo.encodeAuthenticator).MAClen();
+ }
+
+ int dstPos = destination.position();
+ int dstLim = destination.limit();
+ int dstContent = dstPos + headerSize +
+ writeCipher.getExplicitNonceSize();
+ destination.position(dstContent);
+
+ destination.put(memo.fragment);
+
+ destination.limit(destination.position());
+ destination.position(dstContent);
+
+ if ((debug != null) && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", WRITE: " + protocolVersion + " " +
+ Record.contentName(Record.ct_alert) +
+ ", length = " + destination.remaining());
+ }
+
+ // Encrypt the fragment and wrap up a record.
+ long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
+ Record.ct_alert, destination, dstPos, dstLim, headerSize,
+ ProtocolVersion.valueOf(memo.majorVersion,
+ memo.minorVersion), true);
+
+ if ((debug != null) && Debug.isOn("packet")) {
+ ByteBuffer temporary = destination.duplicate();
+ temporary.limit(temporary.position());
+ temporary.position(dstPos);
+ Debug.printHex(
+ "[Raw write]: length = " + temporary.remaining(),
+ temporary);
+ }
+
+ // remain the limit unchanged
+ destination.limit(dstLim);
+
+ return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
+ }
+
+ if (fragmenter != null) {
+ return fragmenter.acquireCiphertext(destination);
+ }
+
+ return null;
+ }
+
+ @Override
+ boolean isEmpty() {
+ return ((fragmenter == null) || fragmenter.isEmpty()) &&
+ ((alertMemos == null) || alertMemos.isEmpty());
+ }
+
+ @Override
+ void initHandshaker() {
+ // clean up
+ fragmenter = null;
+ }
+
+ // buffered record fragment
+ private static class RecordMemo {
+ byte contentType;
+ byte majorVersion;
+ byte minorVersion;
+ int encodeEpoch;
+ CipherBox encodeCipher;
+ Authenticator encodeAuthenticator;
+
+ byte[] fragment;
+ }
+
+ private static class HandshakeMemo extends RecordMemo {
+ byte handshakeType;
+ int messageSequence;
+ int acquireOffset;
+ }
+
+ private final class DTLSFragmenter {
+ private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
+ private int acquireIndex = 0;
+ private int messageSequence = 0;
+ private boolean flightIsReady = false;
+
+ // Per section 4.1.1, RFC 6347:
+ //
+ // If repeated retransmissions do not result in a response, and the
+ // PMTU is unknown, subsequent retransmissions SHOULD back off to a
+ // smaller record size, fragmenting the handshake message as
+ // appropriate.
+ //
+ // In this implementation, two times of retransmits would be attempted
+ // before backing off. The back off is supported only if the packet
+ // size is bigger than 256 bytes.
+ private int retransmits = 2; // attemps of retransmits
+
+ void queueUpChangeCipherSpec() {
+
+ // Cleanup if a new flight starts.
+ if (flightIsReady) {
+ handshakeMemos.clear();
+ acquireIndex = 0;
+ flightIsReady = false;
+ }
+
+ RecordMemo memo = new RecordMemo();
+
+ memo.contentType = Record.ct_change_cipher_spec;
+ memo.majorVersion = protocolVersion.major;
+ memo.minorVersion = protocolVersion.minor;
+ memo.encodeEpoch = writeEpoch;
+ memo.encodeCipher = writeCipher;
+ memo.encodeAuthenticator = writeAuthenticator;
+
+ memo.fragment = new byte[1];
+ memo.fragment[0] = 1;
+
+ handshakeMemos.add(memo);
+ }
+
+ void queueUpHandshake(byte[] buf,
+ int offset, int length) throws IOException {
+
+ // Cleanup if a new flight starts.
+ if (flightIsReady) {
+ handshakeMemos.clear();
+ acquireIndex = 0;
+ flightIsReady = false;
+ }
+
+ HandshakeMemo memo = new HandshakeMemo();
+
+ memo.contentType = Record.ct_handshake;
+ memo.majorVersion = protocolVersion.major;
+ memo.minorVersion = protocolVersion.minor;
+ memo.encodeEpoch = writeEpoch;
+ memo.encodeCipher = writeCipher;
+ memo.encodeAuthenticator = writeAuthenticator;
+
+ memo.handshakeType = buf[offset];
+ memo.messageSequence = messageSequence++;
+ memo.acquireOffset = 0;
+ memo.fragment = new byte[length - 4]; // 4: header size
+ // 1: HandshakeType
+ // 3: message length
+ System.arraycopy(buf, offset + 4, memo.fragment, 0, length - 4);
+
+ handshakeHashing(memo, memo.fragment);
+ handshakeMemos.add(memo);
+
+ if ((memo.handshakeType == HandshakeMessage.ht_client_hello) ||
+ (memo.handshakeType == HandshakeMessage.ht_hello_request) ||
+ (memo.handshakeType ==
+ HandshakeMessage.ht_hello_verify_request) ||
+ (memo.handshakeType == HandshakeMessage.ht_server_hello_done) ||
+ (memo.handshakeType == HandshakeMessage.ht_finished)) {
+
+ flightIsReady = true;
+ }
+ }
+
+ Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
+ if (isEmpty()) {
+ if (isRetransmittable()) {
+ setRetransmission(); // configure for retransmission
+ } else {
+ return null;
+ }
+ }
+
+ RecordMemo memo = handshakeMemos.get(acquireIndex);
+ HandshakeMemo hsMemo = null;
+ if (memo.contentType == Record.ct_handshake) {
+ hsMemo = (HandshakeMemo)memo;
+ }
+
+ int macLen = 0;
+ if (memo.encodeAuthenticator instanceof MAC) {
+ macLen = ((MAC)memo.encodeAuthenticator).MAClen();
+ }
+
+ // ChangeCipherSpec message is pretty small. Don't worry about
+ // the fragmentation of ChangeCipherSpec record.
+ int fragLen;
+ if (packetSize > 0) {
+ fragLen = Math.min(maxRecordSize, packetSize);
+ fragLen = memo.encodeCipher.calculateFragmentSize(
+ fragLen, macLen, 25); // 25: header size
+ // 13: DTLS record
+ // 12: DTLS handshake message
+ fragLen = Math.min(fragLen, Record.maxDataSize);
+ } else {
+ fragLen = Record.maxDataSize;
+ }
+
+ if (fragmentSize > 0) {
+ fragLen = Math.min(fragLen, fragmentSize);
+ }
+
+ int dstPos = dstBuf.position();
+ int dstLim = dstBuf.limit();
+ int dstContent = dstPos + headerSize +
+ memo.encodeCipher.getExplicitNonceSize();
+ dstBuf.position(dstContent);
+
+ if (hsMemo != null) {
+ fragLen = Math.min(fragLen,
+ (hsMemo.fragment.length - hsMemo.acquireOffset));
+
+ dstBuf.put(hsMemo.handshakeType);
+ dstBuf.put((byte)((hsMemo.fragment.length >> 16) & 0xFF));
+ dstBuf.put((byte)((hsMemo.fragment.length >> 8) & 0xFF));
+ dstBuf.put((byte)(hsMemo.fragment.length & 0xFF));
+ dstBuf.put((byte)((hsMemo.messageSequence >> 8) & 0xFF));
+ dstBuf.put((byte)(hsMemo.messageSequence & 0xFF));
+ dstBuf.put((byte)((hsMemo.acquireOffset >> 16) & 0xFF));
+ dstBuf.put((byte)((hsMemo.acquireOffset >> 8) & 0xFF));
+ dstBuf.put((byte)(hsMemo.acquireOffset & 0xFF));
+ dstBuf.put((byte)((fragLen >> 16) & 0xFF));
+ dstBuf.put((byte)((fragLen >> 8) & 0xFF));
+ dstBuf.put((byte)(fragLen & 0xFF));
+ dstBuf.put(hsMemo.fragment, hsMemo.acquireOffset, fragLen);
+ } else {
+ fragLen = Math.min(fragLen, memo.fragment.length);
+ dstBuf.put(memo.fragment, 0, fragLen);
+ }
+
+ dstBuf.limit(dstBuf.position());
+ dstBuf.position(dstContent);
+
+ if ((debug != null) && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", WRITE: " + protocolVersion + " " +
+ Record.contentName(memo.contentType) +
+ ", length = " + dstBuf.remaining());
+ }
+
+ // Encrypt the fragment and wrap up a record.
+ long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
+ memo.contentType, dstBuf,
+ dstPos, dstLim, headerSize,
+ ProtocolVersion.valueOf(memo.majorVersion,
+ memo.minorVersion), true);
+
+ if ((debug != null) && Debug.isOn("packet")) {
+ ByteBuffer temporary = dstBuf.duplicate();
+ temporary.limit(temporary.position());
+ temporary.position(dstPos);
+ Debug.printHex(
+ "[Raw write]: length = " + temporary.remaining(),
+ temporary);
+ }
+
+ // remain the limit unchanged
+ dstBuf.limit(dstLim);
+
+ // Reset the fragmentation offset.
+ if (hsMemo != null) {
+ hsMemo.acquireOffset += fragLen;
+ if (hsMemo.acquireOffset == hsMemo.fragment.length) {
+ acquireIndex++;
+ }
+
+ return new Ciphertext(RecordType.valueOf(
+ hsMemo.contentType, hsMemo.handshakeType), recordSN);
+ } else {
+ acquireIndex++;
+ return new Ciphertext(
+ RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
+ }
+ }
+
+ private void handshakeHashing(HandshakeMemo hsFrag, byte[] hsBody) {
+
+ byte hsType = hsFrag.handshakeType;
+ if ((hsType == HandshakeMessage.ht_hello_request) ||
+ (hsType == HandshakeMessage.ht_hello_verify_request)) {
+
+ // omitted from handshake hash computation
+ return;
+ }
+
+ if ((hsFrag.messageSequence == 0) &&
+ (hsType == HandshakeMessage.ht_client_hello)) {
+
+ // omit initial ClientHello message
+ //
+ // 2: ClientHello.client_version
+ // 32: ClientHello.random
+ int sidLen = hsBody[34];
+
+ if (sidLen == 0) { // empty session_id, initial handshake
+ return;
+ }
+ }
+
+ // calculate the DTLS header
+ byte[] temporary = new byte[12]; // 12: handshake header size
+
+ // Handshake.msg_type
+ temporary[0] = hsFrag.handshakeType;
+
+ // Handshake.length
+ temporary[1] = (byte)((hsBody.length >> 16) & 0xFF);
+ temporary[2] = (byte)((hsBody.length >> 8) & 0xFF);
+ temporary[3] = (byte)(hsBody.length & 0xFF);
+
+ // Handshake.message_seq
+ temporary[4] = (byte)((hsFrag.messageSequence >> 8) & 0xFF);
+ temporary[5] = (byte)(hsFrag.messageSequence & 0xFF);
+
+ // Handshake.fragment_offset
+ temporary[6] = 0;
+ temporary[7] = 0;
+ temporary[8] = 0;
+
+ // Handshake.fragment_length
+ temporary[9] = temporary[1];
+ temporary[10] = temporary[2];
+ temporary[11] = temporary[3];
+
+ if ((hsType != HandshakeMessage.ht_finished) &&
+ (hsType != HandshakeMessage.ht_certificate_verify)) {
+
+ handshakeHash.update(temporary, 0, 12);
+ handshakeHash.update(hsBody, 0, hsBody.length);
+ } else {
+ // Reserve until this handshake message has been processed.
+ handshakeHash.reserve(temporary, 0, 12);
+ handshakeHash.reserve(hsBody, 0, hsBody.length);
+ }
+
+ }
+
+ boolean isEmpty() {
+ if (!flightIsReady || handshakeMemos.isEmpty() ||
+ acquireIndex >= handshakeMemos.size()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ boolean isRetransmittable() {
+ return (flightIsReady && !handshakeMemos.isEmpty() &&
+ (acquireIndex >= handshakeMemos.size()));
+ }
+
+ private void setRetransmission() {
+ acquireIndex = 0;
+ for (RecordMemo memo : handshakeMemos) {
+ if (memo instanceof HandshakeMemo) {
+ HandshakeMemo hmemo = (HandshakeMemo)memo;
+ hmemo.acquireOffset = 0;
+ }
+ }
+
+ // Shrink packet size if:
+ // 1. maximum fragment size is allowed, in which case the packet
+ // size is configured bigger than maxRecordSize;
+ // 2. maximum packet is bigger than 256 bytes;
+ // 3. two times of retransmits have been attempted.
+ if ((packetSize <= maxRecordSize) &&
+ (packetSize > 256) && ((retransmits--) <= 0)) {
+
+ // shrink packet size
+ shrinkPacketSize();
+ retransmits = 2; // attemps of retransmits
+ }
+ }
+
+ private void shrinkPacketSize() {
+ packetSize = Math.max(256, packetSize / 2);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/DTLSRecord.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/**
+ * DTLS record
+ */
+interface DTLSRecord extends Record {
+
+ static final int headerSize = 13; // DTLS record header
+
+ static final int handshakeHeaderSize = 12; // DTLS handshake header
+
+ /*
+ * The size of the header plus the max IV length
+ */
+ static final int headerPlusMaxIVSize =
+ headerSize // header
+ + maxIVLength; // iv
+
+ /*
+ * The maximum size that may be increased when translating plaintext to
+ * ciphertext fragment.
+ */
+ static final int maxPlaintextPlusSize =
+ headerSize // header
+ + maxIVLength // iv
+ + maxMacSize // MAC or AEAD tag
+ + maxPadding; // block cipher padding
+
+ /*
+ * the maximum record size
+ */
+ static final int maxRecordSize =
+ headerPlusMaxIVSize // header + iv
+ + maxDataSize // data
+ + maxPadding // padding
+ + maxMacSize; // MAC or AEAD tag
+
+ /*
+ * For CBC protection in SSL3/TLS1, we break some plaintext into two
+ * packets. Max application data size for the second packet.
+ */
+ static final int maxDataSizeMinusOneByteRecord =
+ maxDataSize // max data size
+ - ( // max one byte record size
+ headerPlusMaxIVSize // header + iv
+ + 1 // one byte data
+ + maxPadding // padding
+ + maxMacSize // MAC
+ );
+
+ /*
+ * Maximum record size for alert and change cipher spec records.
+ * They only contain 2 and 1 bytes of data, respectively.
+ * Allocate a smaller array.
+ */
+ static final int maxAlertRecordSize =
+ headerPlusMaxIVSize // header + iv
+ + 2 // alert
+ + maxPadding // padding
+ + maxMacSize; // MAC
+
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Debug.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Debug.java Thu Jun 04 18:49:37 2015 -0700
@@ -29,6 +29,9 @@
import java.security.AccessController;
import java.util.Locale;
+import sun.misc.HexDumpEncoder;
+import java.nio.ByteBuffer;
+
import sun.security.action.GetPropertyAction;
/**
@@ -198,4 +201,47 @@
static String toString(byte[] b) {
return sun.security.util.Debug.toString(b);
}
+
+ static void printHex(String prefix, byte[] bytes) {
+ HexDumpEncoder dump = new HexDumpEncoder();
+
+ synchronized (System.out) {
+ System.out.println(prefix);
+ try {
+ dump.encodeBuffer(bytes, System.out);
+ } catch (Exception e) {
+ // ignore
+ }
+ System.out.flush();
+ }
+ }
+
+ static void printHex(String prefix, ByteBuffer bb) {
+ HexDumpEncoder dump = new HexDumpEncoder();
+
+ synchronized (System.out) {
+ System.out.println(prefix);
+ try {
+ dump.encodeBuffer(bb.slice(), System.out);
+ } catch (Exception e) {
+ // ignore
+ }
+ System.out.flush();
+ }
+ }
+
+ static void printHex(String prefix, byte[] bytes, int offset, int length) {
+ HexDumpEncoder dump = new HexDumpEncoder();
+
+ synchronized (System.out) {
+ System.out.println(prefix);
+ try {
+ ByteBuffer bb = ByteBuffer.wrap(bytes, offset, length);
+ dump.encodeBuffer(bb, System.out);
+ } catch (Exception e) {
+ // ignore
+ }
+ System.out.flush();
+ }
+ }
}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/EngineArgs.java Thu Jun 04 09:31:49 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,238 +0,0 @@
-/*
- * Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl;
-
-import java.nio.*;
-
-/*
- * A multi-purpose class which handles all of the SSLEngine arguments.
- * It validates arguments, checks for RO conditions, does space
- * calculations, performs scatter/gather, etc.
- *
- * @author Brad R. Wetmore
- */
-class EngineArgs {
-
- /*
- * Keep track of the input parameters.
- */
- ByteBuffer netData;
- ByteBuffer [] appData;
-
- private int offset; // offset/len for the appData array.
- private int len;
-
- /*
- * The initial pos/limit conditions. This is useful because we can
- * quickly calculate the amount consumed/produced in successful
- * operations, or easily return the buffers to their pre-error
- * conditions.
- */
- private int netPos;
- private int netLim;
-
- private int [] appPoss;
- private int [] appLims;
-
- /*
- * Sum total of the space remaining in all of the appData buffers
- */
- private int appRemaining = 0;
-
- private boolean wrapMethod;
-
- /*
- * Called by the SSLEngine.wrap() method.
- */
- EngineArgs(ByteBuffer [] appData, int offset, int len,
- ByteBuffer netData) {
- this.wrapMethod = true;
- init(netData, appData, offset, len);
- }
-
- /*
- * Called by the SSLEngine.unwrap() method.
- */
- EngineArgs(ByteBuffer netData, ByteBuffer [] appData, int offset,
- int len) {
- this.wrapMethod = false;
- init(netData, appData, offset, len);
- }
-
- /*
- * The main initialization method for the arguments. Most
- * of them are pretty obvious as to what they do.
- *
- * Since we're already iterating over appData array for validity
- * checking, we also keep track of how much remainging space is
- * available. Info is used in both unwrap (to see if there is
- * enough space available in the destination), and in wrap (to
- * determine how much more we can copy into the outgoing data
- * buffer.
- */
- private void init(ByteBuffer netData, ByteBuffer [] appData,
- int offset, int len) {
-
- if ((netData == null) || (appData == null)) {
- throw new IllegalArgumentException("src/dst is null");
- }
-
- if ((offset < 0) || (len < 0) || (offset > appData.length - len)) {
- throw new IndexOutOfBoundsException();
- }
-
- if (wrapMethod && netData.isReadOnly()) {
- throw new ReadOnlyBufferException();
- }
-
- netPos = netData.position();
- netLim = netData.limit();
-
- appPoss = new int [appData.length];
- appLims = new int [appData.length];
-
- for (int i = offset; i < offset + len; i++) {
- if (appData[i] == null) {
- throw new IllegalArgumentException(
- "appData[" + i + "] == null");
- }
-
- /*
- * If we're unwrapping, then check to make sure our
- * destination bufffers are writable.
- */
- if (!wrapMethod && appData[i].isReadOnly()) {
- throw new ReadOnlyBufferException();
- }
-
- appRemaining += appData[i].remaining();
-
- appPoss[i] = appData[i].position();
- appLims[i] = appData[i].limit();
- }
-
- /*
- * Ok, looks like we have a good set of args, let's
- * store the rest of this stuff.
- */
- this.netData = netData;
- this.appData = appData;
- this.offset = offset;
- this.len = len;
- }
-
- /*
- * Given spaceLeft bytes to transfer, gather up that much data
- * from the appData buffers (starting at offset in the array),
- * and transfer it into the netData buffer.
- *
- * The user has already ensured there is enough room.
- */
- void gather(int spaceLeft) {
- for (int i = offset; (i < (offset + len)) && (spaceLeft > 0); i++) {
- int amount = Math.min(appData[i].remaining(), spaceLeft);
- appData[i].limit(appData[i].position() + amount);
- netData.put(appData[i]);
- appRemaining -= amount;
- spaceLeft -= amount;
- }
- }
-
- /*
- * Using the supplied buffer, scatter the data into the appData buffers
- * (starting at offset in the array).
- *
- * The user has already ensured there is enough room.
- */
- void scatter(ByteBuffer readyData) {
- int amountLeft = readyData.remaining();
-
- for (int i = offset; (i < (offset + len)) && (amountLeft > 0);
- i++) {
- int amount = Math.min(appData[i].remaining(), amountLeft);
- readyData.limit(readyData.position() + amount);
- appData[i].put(readyData);
- amountLeft -= amount;
- }
- assert(readyData.remaining() == 0);
- }
-
- int getAppRemaining() {
- return appRemaining;
- }
-
- /*
- * Calculate the bytesConsumed/byteProduced. Aren't you glad
- * we saved this off earlier?
- */
- int deltaNet() {
- return (netData.position() - netPos);
- }
-
- /*
- * Calculate the bytesConsumed/byteProduced. Aren't you glad
- * we saved this off earlier?
- */
- int deltaApp() {
- int sum = 0; // Only calculating 2^14 here, don't need a long.
-
- for (int i = offset; i < offset + len; i++) {
- sum += appData[i].position() - appPoss[i];
- }
-
- return sum;
- }
-
- /*
- * In the case of Exception, we want to reset the positions
- * to appear as though no data has been consumed or produced.
- *
- * Currently, this method is only called as we are preparing to
- * fail out, and thus we don't need to actually recalculate
- * appRemaining. If that assumption changes, that variable should
- * be updated here.
- */
- void resetPos() {
- netData.position(netPos);
- for (int i = offset; i < offset + len; i++) {
- // See comment above about recalculating appRemaining.
- appData[i].position(appPoss[i]);
- }
- }
-
- /*
- * We are doing lots of ByteBuffer manipulations, in which case
- * we need to make sure that the limits get set back correctly.
- * This is one of the last things to get done before returning to
- * the user.
- */
- void resetLim() {
- netData.limit(netLim);
- for (int i = offset; i < offset + len; i++) {
- appData[i].limit(appLims[i]);
- }
- }
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/EngineInputRecord.java Thu Jun 04 09:31:49 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,427 +0,0 @@
-/*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-
-package sun.security.ssl;
-
-import java.io.*;
-import java.nio.*;
-import javax.net.ssl.*;
-import javax.crypto.BadPaddingException;
-import sun.misc.HexDumpEncoder;
-
-
-/**
- * Wrapper class around InputRecord.
- *
- * Application data is kept external to the InputRecord,
- * but handshake data (alert/change_cipher_spec/handshake) will
- * be kept internally in the ByteArrayInputStream.
- *
- * @author Brad Wetmore
- */
-final class EngineInputRecord extends InputRecord {
-
- private SSLEngineImpl engine;
-
- /*
- * A dummy ByteBuffer we'll pass back even when the data
- * is stored internally. It'll never actually be used.
- */
- static private ByteBuffer tmpBB = ByteBuffer.allocate(0);
-
- /*
- * Flag to tell whether the last read/parsed data resides
- * internal in the ByteArrayInputStream, or in the external
- * buffers.
- */
- private boolean internalData;
-
- EngineInputRecord(SSLEngineImpl engine) {
- super();
- this.engine = engine;
- }
-
- @Override
- byte contentType() {
- if (internalData) {
- return super.contentType();
- } else {
- return ct_application_data;
- }
- }
-
- /*
- * Check if there is enough inbound data in the ByteBuffer
- * to make a inbound packet. Look for both SSLv2 and SSLv3.
- *
- * @return -1 if there are not enough bytes to tell (small header),
- */
- int bytesInCompletePacket(ByteBuffer buf) throws SSLException {
-
- /*
- * SSLv2 length field is in bytes 0/1
- * SSLv3/TLS length field is in bytes 3/4
- */
- if (buf.remaining() < 5) {
- return -1;
- }
-
- int pos = buf.position();
- byte byteZero = buf.get(pos);
-
- int len = 0;
-
- /*
- * If we have already verified previous packets, we can
- * ignore the verifications steps, and jump right to the
- * determination. Otherwise, try one last hueristic to
- * see if it's SSL/TLS.
- */
- if (formatVerified ||
- (byteZero == ct_handshake) ||
- (byteZero == ct_alert)) {
- /*
- * Last sanity check that it's not a wild record
- */
- ProtocolVersion recordVersion =
- ProtocolVersion.valueOf(buf.get(pos + 1), buf.get(pos + 2));
-
- // check the record version
- checkRecordVersion(recordVersion, false);
-
- /*
- * Reasonably sure this is a V3, disable further checks.
- * We can't do the same in the v2 check below, because
- * read still needs to parse/handle the v2 clientHello.
- */
- formatVerified = true;
-
- /*
- * One of the SSLv3/TLS message types.
- */
- len = ((buf.get(pos + 3) & 0xff) << 8) +
- (buf.get(pos + 4) & 0xff) + headerSize;
-
- } else {
- /*
- * Must be SSLv2 or something unknown.
- * Check if it's short (2 bytes) or
- * long (3) header.
- *
- * Internals can warn about unsupported SSLv2
- */
- boolean isShort = ((byteZero & 0x80) != 0);
-
- if (isShort &&
- ((buf.get(pos + 2) == 1) || buf.get(pos + 2) == 4)) {
-
- ProtocolVersion recordVersion =
- ProtocolVersion.valueOf(buf.get(pos + 3), buf.get(pos + 4));
-
- // check the record version
- checkRecordVersion(recordVersion, true);
-
- /*
- * Client or Server Hello
- */
- int mask = (isShort ? 0x7f : 0x3f);
- len = ((byteZero & mask) << 8) + (buf.get(pos + 1) & 0xff) +
- (isShort ? 2 : 3);
-
- } else {
- // Gobblygook!
- throw new SSLException(
- "Unrecognized SSL message, plaintext connection?");
- }
- }
-
- return len;
- }
-
- /*
- * Pass the data down if it's internally cached, otherwise
- * do it here.
- *
- * If internal data, data is decrypted internally.
- *
- * If external data(app), return a new ByteBuffer with data to
- * process.
- */
- ByteBuffer decrypt(Authenticator authenticator,
- CipherBox box, ByteBuffer bb) throws BadPaddingException {
-
- if (internalData) {
- decrypt(authenticator, box); // MAC is checked during decryption
- return tmpBB;
- }
-
- BadPaddingException reservedBPE = null;
- int tagLen =
- (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0;
- int cipheredLength = bb.remaining();
-
- if (!box.isNullCipher()) {
- try {
- // apply explicit nonce for AEAD/CBC cipher suites if needed
- int nonceSize =
- box.applyExplicitNonce(authenticator, contentType(), bb);
-
- // decrypt the content
- if (box.isAEADMode()) {
- // DON'T encrypt the nonce_explicit for AEAD mode
- bb.position(bb.position() + nonceSize);
- } // The explicit IV for CBC mode can be decrypted.
-
- // Note that the CipherBox.decrypt() does not change
- // the capacity of the buffer.
- box.decrypt(bb, tagLen);
- bb.position(nonceSize); // We don't actually remove the nonce.
- } catch (BadPaddingException bpe) {
- // RFC 2246 states that decryption_failed should be used
- // for this purpose. However, that allows certain attacks,
- // so we just send bad record MAC. We also need to make
- // sure to always check the MAC to avoid a timing attack
- // for the same issue. See paper by Vaudenay et al and the
- // update in RFC 4346/5246.
- //
- // Failover to message authentication code checking.
- reservedBPE = bpe;
- }
- }
-
- // Requires message authentication code for null, stream and block
- // cipher suites.
- if ((authenticator instanceof MAC) && (tagLen != 0)) {
- MAC signer = (MAC)authenticator;
- int macOffset = bb.limit() - tagLen;
-
- // Note that although it is not necessary, we run the same MAC
- // computation and comparison on the payload for both stream
- // cipher and CBC block cipher.
- if (bb.remaining() < tagLen) {
- // negative data length, something is wrong
- if (reservedBPE == null) {
- reservedBPE = new BadPaddingException("bad record");
- }
-
- // set offset of the dummy MAC
- macOffset = cipheredLength - tagLen;
- bb.limit(cipheredLength);
- }
-
- // Run MAC computation and comparison on the payload.
- if (checkMacTags(contentType(), bb, signer, false)) {
- if (reservedBPE == null) {
- reservedBPE = new BadPaddingException("bad record MAC");
- }
- }
-
- // Run MAC computation and comparison on the remainder.
- //
- // It is only necessary for CBC block cipher. It is used to get a
- // constant time of MAC computation and comparison on each record.
- if (box.isCBCMode()) {
- int remainingLen = calculateRemainingLen(
- signer, cipheredLength, macOffset);
-
- // NOTE: here we use the InputRecord.buf because I did not find
- // an effective way to work on ByteBuffer when its capacity is
- // less than remainingLen.
-
- // NOTE: remainingLen may be bigger (less than 1 block of the
- // hash algorithm of the MAC) than the cipheredLength. However,
- // We won't need to worry about it because we always use a
- // maximum buffer for every record. We need a change here if
- // we use small buffer size in the future.
- if (remainingLen > buf.length) {
- // unlikely to happen, just a placehold
- throw new RuntimeException(
- "Internal buffer capacity error");
- }
-
- // Won't need to worry about the result on the remainder. And
- // then we won't need to worry about what's actual data to
- // check MAC tag on. We start the check from the header of the
- // buffer so that we don't need to construct a new byte buffer.
- checkMacTags(contentType(), buf, 0, remainingLen, signer, true);
- }
-
- bb.limit(macOffset);
- }
-
- // Is it a failover?
- if (reservedBPE != null) {
- throw reservedBPE;
- }
-
- return bb.slice();
- }
-
- /*
- * Run MAC computation and comparison
- *
- * Please DON'T change the content of the ByteBuffer parameter!
- */
- private static boolean checkMacTags(byte contentType, ByteBuffer bb,
- MAC signer, boolean isSimulated) {
-
- int position = bb.position();
- int tagLen = signer.MAClen();
- int lim = bb.limit();
- int macData = lim - tagLen;
-
- bb.limit(macData);
- byte[] hash = signer.compute(contentType, bb, isSimulated);
- if (hash == null || tagLen != hash.length) {
- // Something is wrong with MAC implementation.
- throw new RuntimeException("Internal MAC error");
- }
-
- bb.position(macData);
- bb.limit(lim);
- try {
- int[] results = compareMacTags(bb, hash);
- return (results[0] != 0);
- } finally {
- // reset to the data
- bb.position(position);
- bb.limit(macData);
- }
- }
-
- /*
- * A constant-time comparison of the MAC tags.
- *
- * Please DON'T change the content of the ByteBuffer parameter!
- */
- private static int[] compareMacTags(ByteBuffer bb, byte[] tag) {
-
- // An array of hits is used to prevent Hotspot optimization for
- // the purpose of a constant-time check.
- int[] results = {0, 0}; // {missed #, matched #}
-
- // The caller ensures there are enough bytes available in the buffer.
- // So we won't need to check the remaining of the buffer.
- for (int i = 0; i < tag.length; i++) {
- if (bb.get() != tag[i]) {
- results[0]++; // mismatched bytes
- } else {
- results[1]++; // matched bytes
- }
- }
-
- return results;
- }
-
- /*
- * Override the actual write below. We do things this way to be
- * consistent with InputRecord. InputRecord may try to write out
- * data to the peer, and *then* throw an Exception. This forces
- * data to be generated/output before the exception is ever
- * generated.
- */
- @Override
- void writeBuffer(OutputStream s, byte [] buf, int off, int len)
- throws IOException {
- /*
- * Copy data out of buffer, it's ready to go.
- */
- ByteBuffer netBB = ByteBuffer.allocate(len).put(buf, 0, len).flip();
- engine.writer.putOutboundDataSync(netBB);
- }
-
- /*
- * Delineate or read a complete packet from src.
- *
- * If internal data (hs, alert, ccs), the data is read and
- * stored internally.
- *
- * If external data (app), return a new ByteBuffer which points
- * to the data to process.
- */
- ByteBuffer read(ByteBuffer srcBB) throws IOException {
- /*
- * Could have a src == null/dst == null check here,
- * but that was already checked by SSLEngine.unwrap before
- * ever attempting to read.
- */
-
- /*
- * If we have anything besides application data,
- * or if we haven't even done the initial v2 verification,
- * we send this down to be processed by the underlying
- * internal cache.
- */
- if (!formatVerified ||
- (srcBB.get(srcBB.position()) != ct_application_data)) {
- internalData = true;
- read(new ByteBufferInputStream(srcBB), (OutputStream) null);
- return tmpBB;
- }
-
- internalData = false;
-
- int srcPos = srcBB.position();
- int srcLim = srcBB.limit();
-
- ProtocolVersion recordVersion = ProtocolVersion.valueOf(
- srcBB.get(srcPos + 1), srcBB.get(srcPos + 2));
-
- // check the record version
- checkRecordVersion(recordVersion, false);
-
- /*
- * It's really application data. How much to consume?
- * Jump over the header.
- */
- int len = bytesInCompletePacket(srcBB);
- assert(len > 0);
-
- if (debug != null && Debug.isOn("packet")) {
- try {
- HexDumpEncoder hd = new HexDumpEncoder();
- ByteBuffer bb = srcBB.duplicate(); // Use copy of BB
- bb.limit(srcPos + len);
-
- System.out.println("[Raw read (bb)]: length = " + len);
- hd.encodeBuffer(bb, System.out);
- } catch (IOException e) { }
- }
-
- // Demarcate past header to end of packet.
- srcBB.position(srcPos + headerSize);
- srcBB.limit(srcPos + len);
-
- // Protect remainder of buffer, create slice to actually
- // operate on.
- ByteBuffer bb = srcBB.slice();
-
- srcBB.position(srcBB.limit());
- srcBB.limit(srcLim);
-
- return bb;
- }
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/EngineOutputRecord.java Thu Jun 04 09:31:49 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,329 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-
-package sun.security.ssl;
-
-import java.io.*;
-import java.nio.*;
-
-/**
- * A OutputRecord class extension which uses external ByteBuffers
- * or the internal ByteArrayOutputStream for data manipulations.
- * <P>
- * Instead of rewriting this entire class
- * to use ByteBuffers, we leave things intact, so handshake, CCS,
- * and alerts will continue to use the internal buffers, but application
- * data will use external buffers.
- *
- * @author Brad Wetmore
- */
-final class EngineOutputRecord extends OutputRecord {
-
- private SSLEngineImpl engine;
- private EngineWriter writer;
-
- private boolean finishedMsg = false;
-
- /*
- * All handshake hashing is done by the superclass
- */
-
- /*
- * Default constructor makes a record supporting the maximum
- * SSL record size. It allocates the header bytes directly.
- *
- * @param type the content type for the record
- */
- EngineOutputRecord(byte type, SSLEngineImpl engine) {
- super(type, recordSize(type));
- this.engine = engine;
- writer = engine.writer;
- }
-
- /**
- * Get the size of the buffer we need for records of the specified
- * type.
- * <P>
- * Application data buffers will provide their own byte buffers,
- * and will not use the internal byte caching.
- */
- private static int recordSize(byte type) {
- switch (type) {
-
- case ct_change_cipher_spec:
- case ct_alert:
- return maxAlertRecordSize;
-
- case ct_handshake:
- return maxRecordSize;
-
- case ct_application_data:
- return 0;
- }
-
- throw new RuntimeException("Unknown record type: " + type);
- }
-
- void setFinishedMsg() {
- finishedMsg = true;
- }
-
- @Override
- public void flush() throws IOException {
- finishedMsg = false;
- }
-
- boolean isFinishedMsg() {
- return finishedMsg;
- }
-
- /*
- * Override the actual write below. We do things this way to be
- * consistent with InputRecord. InputRecord may try to write out
- * data to the peer, and *then* throw an Exception. This forces
- * data to be generated/output before the exception is ever
- * generated.
- */
- @Override
- void writeBuffer(OutputStream s, byte [] buf, int off, int len,
- int debugOffset) throws IOException {
- /*
- * Copy data out of buffer, it's ready to go.
- */
- ByteBuffer netBB = ByteBuffer.allocate(len).put(buf, off, len).flip();
- writer.putOutboundData(netBB);
- }
-
- /*
- * Main method for writing non-application data.
- * We MAC/encrypt, then send down for processing.
- */
- void write(Authenticator authenticator, CipherBox writeCipher)
- throws IOException {
-
- /*
- * Sanity check.
- */
- switch (contentType()) {
- case ct_change_cipher_spec:
- case ct_alert:
- case ct_handshake:
- break;
- default:
- throw new RuntimeException("unexpected byte buffers");
- }
-
- /*
- * Don't bother to really write empty records. We went this
- * far to drive the handshake machinery, for correctness; not
- * writing empty records improves performance by cutting CPU
- * time and network resource usage. Also, some protocol
- * implementations are fragile and don't like to see empty
- * records, so this increases robustness.
- *
- * (Even change cipher spec messages have a byte of data!)
- */
- if (!isEmpty()) {
- // compress(); // eventually
- encrypt(authenticator, writeCipher);
-
- // send down for processing
- write((OutputStream)null, false, (ByteArrayOutputStream)null);
- }
- return;
- }
-
- /**
- * Main wrap/write driver.
- */
- void write(EngineArgs ea, Authenticator authenticator,
- CipherBox writeCipher) throws IOException {
- /*
- * sanity check to make sure someone didn't inadvertantly
- * send us an impossible combination we don't know how
- * to process.
- */
- assert(contentType() == ct_application_data);
-
- /*
- * Have we set the MAC's yet? If not, we're not ready
- * to process application data yet.
- */
- if (authenticator == MAC.NULL) {
- return;
- }
-
- /*
- * Don't bother to really write empty records. We went this
- * far to drive the handshake machinery, for correctness; not
- * writing empty records improves performance by cutting CPU
- * time and network resource usage. Also, some protocol
- * implementations are fragile and don't like to see empty
- * records, so this increases robustness.
- */
- if (ea.getAppRemaining() == 0) {
- return;
- }
-
- /*
- * By default, we counter chosen plaintext issues on CBC mode
- * ciphersuites in SSLv3/TLS1.0 by sending one byte of application
- * data in the first record of every payload, and the rest in
- * subsequent record(s). Note that the issues have been solved in
- * TLS 1.1 or later.
- *
- * It is not necessary to split the very first application record of
- * a freshly negotiated TLS session, as there is no previous
- * application data to guess. To improve compatibility, we will not
- * split such records.
- *
- * Because of the compatibility, we'd better produce no more than
- * SSLSession.getPacketBufferSize() net data for each wrap. As we
- * need a one-byte record at first, the 2nd record size should be
- * equal to or less than Record.maxDataSizeMinusOneByteRecord.
- *
- * This avoids issues in the outbound direction. For a full fix,
- * the peer must have similar protections.
- */
- int length;
- if (engine.needToSplitPayload(writeCipher, protocolVersion)) {
- write(ea, authenticator, writeCipher, 0x01);
- ea.resetLim(); // reset application data buffer limit
- length = Math.min(ea.getAppRemaining(),
- maxDataSizeMinusOneByteRecord);
- } else {
- length = Math.min(ea.getAppRemaining(), maxDataSize);
- }
-
- // Don't bother to really write empty records.
- if (length > 0) {
- write(ea, authenticator, writeCipher, length);
- }
-
- return;
- }
-
- void write(EngineArgs ea, Authenticator authenticator,
- CipherBox writeCipher, int length) throws IOException {
- /*
- * Copy out existing buffer values.
- */
- ByteBuffer dstBB = ea.netData;
- int dstPos = dstBB.position();
- int dstLim = dstBB.limit();
-
- /*
- * Where to put the data. Jump over the header.
- *
- * Don't need to worry about SSLv2 rewrites, if we're here,
- * that's long since done.
- */
- int dstData = dstPos + headerSize + writeCipher.getExplicitNonceSize();
- dstBB.position(dstData);
-
- /*
- * transfer application data into the network data buffer
- */
- ea.gather(length);
- dstBB.limit(dstBB.position());
- dstBB.position(dstData);
-
- /*
- * "flip" but skip over header again, add MAC & encrypt
- */
- if (authenticator instanceof MAC) {
- MAC signer = (MAC)authenticator;
- if (signer.MAClen() != 0) {
- byte[] hash = signer.compute(contentType(), dstBB, false);
-
- /*
- * position was advanced to limit in compute above.
- *
- * Mark next area as writable (above layers should have
- * established that we have plenty of room), then write
- * out the hash.
- */
- dstBB.limit(dstBB.limit() + hash.length);
- dstBB.put(hash);
-
- // reset the position and limit
- dstBB.limit(dstBB.position());
- dstBB.position(dstData);
- }
- }
-
- if (!writeCipher.isNullCipher()) {
- /*
- * Requires explicit IV/nonce for CBC/AEAD cipher suites for TLS 1.1
- * or later.
- */
- if (protocolVersion.v >= ProtocolVersion.TLS11.v &&
- (writeCipher.isCBCMode() || writeCipher.isAEADMode())) {
- byte[] nonce = writeCipher.createExplicitNonce(
- authenticator, contentType(), dstBB.remaining());
- dstBB.position(dstPos + headerSize);
- dstBB.put(nonce);
- if (!writeCipher.isAEADMode()) {
- // The explicit IV in TLS 1.1 and later can be encrypted.
- dstBB.position(dstPos + headerSize);
- } // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
- }
-
- /*
- * Encrypt may pad, so again the limit may have changed.
- */
- writeCipher.encrypt(dstBB, dstLim);
-
- if ((debug != null) && (Debug.isOn("record") ||
- (Debug.isOn("handshake") &&
- (contentType() == ct_change_cipher_spec)))) {
- System.out.println(Thread.currentThread().getName()
- // v3.0/v3.1 ...
- + ", WRITE: " + protocolVersion
- + " " + InputRecord.contentName(contentType())
- + ", length = " + length);
- }
- } else {
- dstBB.position(dstBB.limit());
- }
-
- int packetLength = dstBB.limit() - dstPos - headerSize;
-
- /*
- * Finish out the record header.
- */
- dstBB.put(dstPos, contentType());
- dstBB.put(dstPos + 1, protocolVersion.major);
- dstBB.put(dstPos + 2, protocolVersion.minor);
- dstBB.put(dstPos + 3, (byte)(packetLength >> 8));
- dstBB.put(dstPos + 4, (byte)packetLength);
-
- /*
- * Position was already set by encrypt() above.
- */
- dstBB.limit(dstLim);
- }
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/EngineWriter.java Thu Jun 04 09:31:49 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,244 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.LinkedList;
-import javax.net.ssl.SSLEngineResult.HandshakeStatus;
-import sun.misc.HexDumpEncoder;
-
-/**
- * A class to help abstract away SSLEngine writing synchronization.
- */
-final class EngineWriter {
-
- /*
- * Outgoing handshake Data waiting for a ride is stored here.
- * Normal application data is written directly into the outbound
- * buffer, but handshake data can be written out at any time,
- * so we have buffer it somewhere.
- *
- * When wrap is called, we first check to see if there is
- * any data waiting, then if we're in a data transfer state,
- * we try to write app data.
- *
- * This will contain either ByteBuffers, or the marker
- * HandshakeStatus.FINISHED to signify that a handshake just completed.
- */
- private LinkedList<Object> outboundList;
-
- private boolean outboundClosed = false;
-
- /* Class and subclass dynamic debugging support */
- private static final Debug debug = Debug.getInstance("ssl");
-
- EngineWriter() {
- outboundList = new LinkedList<Object>();
- }
-
- /*
- * Upper levels assured us we had room for at least one packet of data.
- * As per the SSLEngine spec, we only return one SSL packets worth of
- * data.
- */
- private HandshakeStatus getOutboundData(ByteBuffer dstBB) {
-
- Object msg = outboundList.removeFirst();
- assert(msg instanceof ByteBuffer);
-
- ByteBuffer bbIn = (ByteBuffer) msg;
- assert(dstBB.remaining() >= bbIn.remaining());
-
- dstBB.put(bbIn);
-
- /*
- * If we have more data in the queue, it's either
- * a finished message, or an indication that we need
- * to call wrap again.
- */
- if (hasOutboundDataInternal()) {
- msg = outboundList.getFirst();
- if (msg == HandshakeStatus.FINISHED) {
- outboundList.removeFirst(); // consume the message
- return HandshakeStatus.FINISHED;
- } else {
- return HandshakeStatus.NEED_WRAP;
- }
- } else {
- return null;
- }
- }
-
- /*
- * Properly orders the output of the data written to the wrap call.
- * This is only handshake data, application data goes through the
- * other writeRecord.
- */
- synchronized void writeRecord(EngineOutputRecord outputRecord,
- Authenticator authenticator,
- CipherBox writeCipher) throws IOException {
-
- /*
- * Only output if we're still open.
- */
- if (outboundClosed) {
- throw new IOException("writer side was already closed.");
- }
-
- outputRecord.write(authenticator, writeCipher);
-
- /*
- * Did our handshakers notify that we just sent the
- * Finished message?
- *
- * Add an "I'm finished" message to the queue.
- */
- if (outputRecord.isFinishedMsg()) {
- outboundList.addLast(HandshakeStatus.FINISHED);
- }
- }
-
- /*
- * Output the packet info.
- */
- private void dumpPacket(EngineArgs ea, boolean hsData) {
- try {
- HexDumpEncoder hd = new HexDumpEncoder();
-
- ByteBuffer bb = ea.netData.duplicate();
-
- int pos = bb.position();
- bb.position(pos - ea.deltaNet());
- bb.limit(pos);
-
- System.out.println("[Raw write" +
- (hsData ? "" : " (bb)") + "]: length = " +
- bb.remaining());
- hd.encodeBuffer(bb, System.out);
- } catch (IOException e) { }
- }
-
- /*
- * Properly orders the output of the data written to the wrap call.
- * Only app data goes through here, handshake data goes through
- * the other writeRecord.
- *
- * Shouldn't expect to have an IOException here.
- *
- * Return any determined status.
- */
- synchronized HandshakeStatus writeRecord(
- EngineOutputRecord outputRecord, EngineArgs ea,
- Authenticator authenticator,
- CipherBox writeCipher) throws IOException {
-
- /*
- * If we have data ready to go, output this first before
- * trying to consume app data.
- */
- if (hasOutboundDataInternal()) {
- HandshakeStatus hss = getOutboundData(ea.netData);
-
- if (debug != null && Debug.isOn("packet")) {
- /*
- * We could have put the dump in
- * OutputRecord.write(OutputStream), but let's actually
- * output when it's actually output by the SSLEngine.
- */
- dumpPacket(ea, true);
- }
-
- return hss;
- }
-
- /*
- * If we are closed, no more app data can be output.
- * Only existing handshake data (above) can be obtained.
- */
- if (outboundClosed) {
- throw new IOException("The write side was already closed");
- }
-
- outputRecord.write(ea, authenticator, writeCipher);
-
- if (debug != null && Debug.isOn("packet")) {
- dumpPacket(ea, false);
- }
-
- /*
- * No way new outbound handshake data got here if we're
- * locked properly.
- *
- * We don't have any status we can return.
- */
- return null;
- }
-
- /*
- * We already hold "this" lock, this is the callback from the
- * outputRecord.write() above. We already know this
- * writer can accept more data (outboundClosed == false),
- * and the closure is sync'd.
- */
- void putOutboundData(ByteBuffer bytes) {
- outboundList.addLast(bytes);
- }
-
- /*
- * This is for the really rare case that someone is writing from
- * the *InputRecord* before we know what to do with it.
- */
- synchronized void putOutboundDataSync(ByteBuffer bytes)
- throws IOException {
-
- if (outboundClosed) {
- throw new IOException("Write side already closed");
- }
-
- outboundList.addLast(bytes);
- }
-
- /*
- * Non-synch'd version of this method, called by internals
- */
- private boolean hasOutboundDataInternal() {
- return (outboundList.size() != 0);
- }
-
- synchronized boolean hasOutboundData() {
- return hasOutboundDataInternal();
- }
-
- synchronized boolean isOutboundDone() {
- return outboundClosed && !hasOutboundDataInternal();
- }
-
- synchronized void closeOutbound() {
- outboundClosed = true;
- }
-
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ExtensionType.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ExtensionType.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -43,7 +43,8 @@
return name;
}
- static List<ExtensionType> knownExtensions = new ArrayList<ExtensionType>(9);
+ static List<ExtensionType> knownExtensions =
+ new ArrayList<ExtensionType>(13);
static ExtensionType get(int id) {
for (ExtensionType ext : knownExtensions) {
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java Thu Jun 04 18:49:37 2015 -0700
@@ -29,6 +29,7 @@
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.util.Locale;
+import java.nio.ByteBuffer;
/**
* Abstraction for the SSL/TLS hash of all handshake messages that is
@@ -99,6 +100,9 @@
// For TLS 1.2
private MessageDigest finMD;
+ // Cache for input record handshake hash computation
+ private ByteArrayOutputStream reserve = new ByteArrayOutputStream();
+
/**
* Create a new HandshakeHash. needCertificateVerify indicates whether
* a hash for the certificate verify message is required.
@@ -107,7 +111,106 @@
clonesNeeded = needCertificateVerify ? 3 : 2;
}
+ void reserve(ByteBuffer input) {
+ if (input.hasArray()) {
+ reserve.write(input.array(),
+ input.position() + input.arrayOffset(), input.remaining());
+ } else {
+ int inPos = input.position();
+ byte[] holder = new byte[input.remaining()];
+ input.get(holder);
+ input.position(inPos);
+ reserve.write(holder, 0, holder.length);
+ }
+ }
+
+ void reserve(byte[] b, int offset, int len) {
+ reserve.write(b, offset, len);
+ }
+
+ void reload() {
+ if (reserve.size() != 0) {
+ byte[] bytes = reserve.toByteArray();
+ reserve.reset();
+ update(bytes, 0, bytes.length);
+ }
+ }
+
+ void update(ByteBuffer input) {
+
+ // reload if there are reserved messages.
+ reload();
+
+ int inPos = input.position();
+ switch (version) {
+ case 1:
+ md5.update(input);
+ input.position(inPos);
+
+ sha.update(input);
+ input.position(inPos);
+
+ break;
+ default:
+ if (finMD != null) {
+ finMD.update(input);
+ input.position(inPos);
+ }
+ if (input.hasArray()) {
+ data.write(input.array(),
+ inPos + input.arrayOffset(), input.remaining());
+ } else {
+ byte[] holder = new byte[input.remaining()];
+ input.get(holder);
+ input.position(inPos);
+ data.write(holder, 0, holder.length);
+ }
+ break;
+ }
+ }
+
+ void update(byte handshakeType, byte[] handshakeBody) {
+
+ // reload if there are reserved messages.
+ reload();
+
+ switch (version) {
+ case 1:
+ md5.update(handshakeType);
+ sha.update(handshakeType);
+
+ md5.update((byte)((handshakeBody.length >> 16) & 0xFF));
+ sha.update((byte)((handshakeBody.length >> 16) & 0xFF));
+ md5.update((byte)((handshakeBody.length >> 8) & 0xFF));
+ sha.update((byte)((handshakeBody.length >> 8) & 0xFF));
+ md5.update((byte)(handshakeBody.length & 0xFF));
+ sha.update((byte)(handshakeBody.length & 0xFF));
+
+ md5.update(handshakeBody);
+ sha.update(handshakeBody);
+ break;
+ default:
+ if (finMD != null) {
+ finMD.update(handshakeType);
+ finMD.update((byte)((handshakeBody.length >> 16) & 0xFF));
+ finMD.update((byte)((handshakeBody.length >> 8) & 0xFF));
+ finMD.update((byte)(handshakeBody.length & 0xFF));
+ finMD.update(handshakeBody);
+ }
+ data.write(handshakeType);
+ data.write((byte)((handshakeBody.length >> 16) & 0xFF));
+ data.write((byte)((handshakeBody.length >> 8) & 0xFF));
+ data.write((byte)(handshakeBody.length & 0xFF));
+ data.write(handshakeBody, 0, handshakeBody.length);
+ break;
+ }
+ }
+
void update(byte[] b, int offset, int len) {
+
+ // reload if there are reserved messages.
+ reload();
+
switch (version) {
case 1:
md5.update(b, offset, len);
@@ -139,9 +242,15 @@
void protocolDetermined(ProtocolVersion pv) {
// Do not set again, will ignore
- if (version != -1) return;
+ if (version != -1) {
+ return;
+ }
- version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1;
+ if (pv.maybeDTLSProtocol()) {
+ version = pv.compareTo(ProtocolVersion.DTLS12) >= 0 ? 2 : 1;
+ } else {
+ version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1;
+ }
switch (version) {
case 1:
// initiate md5, sha and call update on saved array
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeInStream.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeInStream.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,11 +23,11 @@
* questions.
*/
-
package sun.security.ssl;
-import java.io.InputStream;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.nio.ByteBuffer;
import javax.net.ssl.SSLException;
@@ -38,154 +38,104 @@
* Once a new handshake record arrives, it is buffered in this class until
* processed by the Handshaker. The buffer may also contain incomplete
* handshake messages in case the message is split across multiple records.
- * Handshaker.process_record deals with all that. It may also contain
+ * Handshaker.processRecord deals with all that. It may also contain
* handshake messages larger than the default buffer size (e.g. large
- * certificate messages). The buffer is grown dynamically to handle that
- * (see InputRecord.queueHandshake()).
+ * certificate messages). The buffer is grown dynamically to handle that.
*
- * Note that the InputRecord used as a buffer here is separate from the
- * AppInStream.r, which is where data from the socket is initially read
- * into. This is because once the initial handshake has been completed,
- * handshake and application data messages may be interleaved arbitrarily
- * and must be processed independently.
+ * Note that this class only handles Handshake messages in TLS format.
+ * DTLS Handshake messages should be converted into TLS format before
+ * calling into this method.
*
* @author David Brownell
*/
-public class HandshakeInStream extends InputStream {
- InputRecord r;
+// This class is used to handle plain text handshake messages.
+//
+public final class HandshakeInStream extends ByteArrayInputStream {
/*
* Construct the stream; we'll be accumulating hashes of the
* input records using two sets of digests.
*/
- HandshakeInStream(HandshakeHash handshakeHash) {
- r = new InputRecord();
- r.setHandshakeHash(handshakeHash);
- }
-
-
- // overridden InputStream methods
-
- /*
- * Return the number of bytes available for read().
- *
- * Note that this returns the bytes remaining in the buffer, not
- * the bytes remaining in the current handshake message.
- */
- @Override
- public int available() {
- return r.available();
- }
-
- /*
- * Get a byte of handshake data.
- */
- @Override
- public int read() throws IOException {
- int n = r.read();
- if (n == -1) {
- throw new SSLException("Unexpected end of handshake data");
- }
- return n;
+ HandshakeInStream() {
+ super(new byte[0]); // lazy to alloacte the internal buffer
}
- /*
- * Get a bunch of bytes of handshake data.
- */
+ //
+ // overridden ByteArrayInputStream methods
+ //
+
@Override
- public int read(byte b [], int off, int len) throws IOException {
- // we read from a ByteArrayInputStream, it always returns the
- // data in a single read if enough is available
- int n = r.read(b, off, len);
- if (n != len) {
+ public int read(byte[] b) throws IOException {
+ if (super.read(b) != b.length) {
throw new SSLException("Unexpected end of handshake data");
}
- return n;
- }
- /*
- * Skip some handshake data.
- */
- @Override
- public long skip(long n) throws IOException {
- return r.skip(n);
+ return b.length;
}
- /*
- * Mark/ reset code, implemented using InputRecord mark/ reset.
- *
- * Note that it currently provides only a limited mark functionality
- * and should be used with care (once a new handshake record has been
- * read, data that has already been consumed is lost even if marked).
- */
-
- @Override
- public void mark(int readlimit) {
- r.mark(readlimit);
- }
-
- @Override
- public void reset() throws IOException {
- r.reset();
- }
-
- @Override
- public boolean markSupported() {
- return true;
- }
-
-
- // handshake management functions
+ //
+ // handshake input stream management functions
+ //
/*
* Here's an incoming record with handshake data. Queue the contents;
* it might be one or more entire messages, complete a message that's
* partly queued, or both.
*/
- void incomingRecord(InputRecord in) throws IOException {
- r.queueHandshake(in);
+ void incomingRecord(ByteBuffer in) throws IOException {
+ int len;
+
+ // Move any unread data to the front of the buffer.
+ if (pos != 0) {
+ len = count - pos;
+ if (len != 0) {
+ System.arraycopy(buf, pos, buf, 0, len);
+ }
+ pos = 0;
+ count = len;
+ }
+
+ // Grow buffer if needed.
+ len = in.remaining() + count;
+ if (buf.length < len) {
+ byte[] newbuf = new byte[len];
+ if (count != 0) {
+ System.arraycopy(buf, 0, newbuf, 0, count);
+ }
+ buf = newbuf;
+ }
+
+ // Append the incoming record to the buffer
+ in.get(buf, count, in.remaining());
+ count = len;
}
- /*
- * Hash any data we've consumed but not yet hashed. Useful mostly
- * for processing client certificate messages (so we can check the
- * immediately following cert verify message) and finished messages
- * (so we can compute our own finished message).
- */
- void digestNow() {
- r.doHashes();
- }
-
- /*
- * Do more than skip that handshake data ... totally ignore it.
- * The difference is that the data does not get hashed.
- */
- void ignore(int n) {
- r.ignore(n);
- }
-
-
+ //
// Message parsing methods
+ //
/*
* Read 8, 16, 24, and 32 bit SSL integer data types, encoded
* in standard big-endian form.
*/
-
int getInt8() throws IOException {
+ verifyLength(1);
return read();
}
int getInt16() throws IOException {
+ verifyLength(2);
return (getInt8() << 8) | getInt8();
}
int getInt24() throws IOException {
+ verifyLength(3);
return (getInt8() << 16) | (getInt8() << 8) | getInt8();
}
int getInt32() throws IOException {
+ verifyLength(4);
return (getInt8() << 24) | (getInt8() << 16)
| (getInt8() << 8) | getInt8();
}
@@ -193,13 +143,12 @@
/*
* Read byte vectors with 8, 16, and 24 bit length encodings.
*/
-
byte[] getBytes8() throws IOException {
int len = getInt8();
verifyLength(len);
byte b[] = new byte[len];
- read(b, 0, len);
+ read(b);
return b;
}
@@ -208,7 +157,7 @@
verifyLength(len);
byte b[] = new byte[len];
- read(b, 0, len);
+ read(b);
return b;
}
@@ -217,16 +166,14 @@
verifyLength(len);
byte b[] = new byte[len];
- read(b, 0, len);
+ read(b);
return b;
}
// Is a length greater than available bytes in the record?
private void verifyLength(int len) throws SSLException {
if (len > available()) {
- throw new SSLException(
- "Not enough data to fill declared vector size");
+ throw new SSLException("Unexpected end of handshake data");
}
}
-
}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java Thu Jun 04 18:49:37 2015 -0700
@@ -73,24 +73,43 @@
*/
public abstract class HandshakeMessage {
- HandshakeMessage() { }
+ /* Class and subclass dynamic debugging support */
+ public static final Debug debug = Debug.getInstance("ssl");
// enum HandshakeType:
- static final byte ht_hello_request = 0;
- static final byte ht_client_hello = 1;
- static final byte ht_server_hello = 2;
+ static final byte ht_hello_request = 0; // RFC 5246
+ static final byte ht_client_hello = 1; // RFC 5246
+ static final byte ht_server_hello = 2; // RFC 5246
+ static final byte ht_hello_verify_request = 3; // RFC 6347
+ static final byte ht_new_session_ticket = 4; // RFC 4507
+
+ static final byte ht_certificate = 11; // RFC 5246
+ static final byte ht_server_key_exchange = 12; // RFC 5246
+ static final byte ht_certificate_request = 13; // RFC 5246
+ static final byte ht_server_hello_done = 14; // RFC 5246
+ static final byte ht_certificate_verify = 15; // RFC 5246
+ static final byte ht_client_key_exchange = 16; // RFC 5246
- static final byte ht_certificate = 11;
- static final byte ht_server_key_exchange = 12;
- static final byte ht_certificate_request = 13;
- static final byte ht_server_hello_done = 14;
- static final byte ht_certificate_verify = 15;
- static final byte ht_client_key_exchange = 16;
+ static final byte ht_finished = 20; // RFC 5246
+ static final byte ht_certificate_url = 21; // RFC 6066
+ static final byte ht_certificate_status = 22; // RFC 6066
+ static final byte ht_supplemental_data = 23; // RFC 4680
+
+ static final byte ht_not_applicable = -1; // N/A
- static final byte ht_finished = 20;
+ /*
+ * SSL 3.0 MAC padding constants.
+ * Also used by CertificateVerify and Finished during the handshake.
+ */
+ static final byte[] MD5_pad1 = genPad(0x36, 48);
+ static final byte[] MD5_pad2 = genPad(0x5c, 48);
- /* Class and subclass dynamic debugging support */
- public static final Debug debug = Debug.getInstance("ssl");
+ static final byte[] SHA_pad1 = genPad(0x36, 40);
+ static final byte[] SHA_pad2 = genPad(0x5c, 40);
+
+ // default constructor
+ HandshakeMessage() {
+ }
/**
* Utility method to convert a BigInteger to a byte array in unsigned
@@ -109,16 +128,6 @@
return b;
}
- /*
- * SSL 3.0 MAC padding constants.
- * Also used by CertificateVerify and Finished during the handshake.
- */
- static final byte[] MD5_pad1 = genPad(0x36, 48);
- static final byte[] MD5_pad2 = genPad(0x5c, 48);
-
- static final byte[] SHA_pad1 = genPad(0x36, 40);
- static final byte[] SHA_pad2 = genPad(0x5c, 40);
-
private static byte[] genPad(int b, int count) {
byte[] padding = new byte[count];
Arrays.fill(padding, (byte)b);
@@ -141,6 +150,7 @@
s.write(messageType());
s.putInt24(len);
send(s);
+ s.complete();
}
/*
@@ -199,6 +209,69 @@
}
+/*
+ * HelloVerifyRequest ... SERVER --> CLIENT [DTLS only]
+ *
+ * The definition of HelloVerifyRequest is as follows:
+ *
+ * struct {
+ * ProtocolVersion server_version;
+ * opaque cookie<0..2^8-1>;
+ * } HelloVerifyRequest;
+ *
+ * For DTLS protocols, once the client has transmitted the ClientHello message,
+ * it expects to see a HelloVerifyRequest from the server. However, if the
+ * server's message is lost, the client knows that either the ClientHello or
+ * the HelloVerifyRequest has been lost and retransmits. [RFC 6347]
+ */
+static final class HelloVerifyRequest extends HandshakeMessage {
+ ProtocolVersion protocolVersion;
+ byte[] cookie; // 1 to 2^8 - 1 bytes
+
+ HelloVerifyRequest(HelloCookieManager helloCookieManager,
+ ClientHello clientHelloMsg) {
+
+ this.protocolVersion = clientHelloMsg.protocolVersion;
+ this.cookie = helloCookieManager.getCookie(clientHelloMsg);
+ }
+
+ HelloVerifyRequest(
+ HandshakeInStream input, int messageLength) throws IOException {
+
+ this.protocolVersion =
+ ProtocolVersion.valueOf(input.getInt8(), input.getInt8());
+ this.cookie = input.getBytes8();
+
+ // Is it a valid cookie?
+ HelloCookieManager.checkCookie(protocolVersion, cookie);
+ }
+
+ @Override
+ int messageType() {
+ return ht_hello_verify_request;
+ }
+
+ @Override
+ int messageLength() {
+ return 2 + cookie.length; // 2: the length of protocolVersion
+ }
+
+ @Override
+ void send(HandshakeOutStream hos) throws IOException {
+ hos.putInt8(protocolVersion.major);
+ hos.putInt8(protocolVersion.minor);
+ hos.putBytes8(cookie);
+ }
+
+ @Override
+ void print(PrintStream out) throws IOException {
+ out.println("*** HelloVerifyRequest");
+ if (debug != null && Debug.isOn("verbose")) {
+ out.println("server_version: " + protocolVersion);
+ Debug.println(out, "cookie", cookie);
+ }
+ }
+}
/*
* ClientHello ... CLIENT --> SERVER
@@ -213,22 +286,31 @@
*/
static final class ClientHello extends HandshakeMessage {
- ProtocolVersion protocolVersion;
- RandomCookie clnt_random;
- SessionId sessionId;
- private CipherSuiteList cipherSuites;
- byte[] compression_methods;
+ ProtocolVersion protocolVersion;
+ RandomCookie clnt_random;
+ SessionId sessionId;
+ byte[] cookie; // DTLS only
+ private CipherSuiteList cipherSuites;
+ private final boolean isDTLS;
+ byte[] compression_methods;
HelloExtensions extensions = new HelloExtensions();
private final static byte[] NULL_COMPRESSION = new byte[] {0};
ClientHello(SecureRandom generator, ProtocolVersion protocolVersion,
- SessionId sessionId, CipherSuiteList cipherSuites) {
+ SessionId sessionId, CipherSuiteList cipherSuites,
+ boolean isDTLS) {
+ this.isDTLS = isDTLS;
this.protocolVersion = protocolVersion;
this.sessionId = sessionId;
this.cipherSuites = cipherSuites;
+ if (isDTLS) {
+ this.cookie = new byte[0];
+ } else {
+ this.cookie = null;
+ }
if (cipherSuites.containsEC()) {
extensions.add(SupportedEllipticCurvesExtension.DEFAULT);
@@ -239,11 +321,21 @@
compression_methods = NULL_COMPRESSION;
}
- ClientHello(HandshakeInStream s, int messageLength) throws IOException {
+ ClientHello(HandshakeInStream s,
+ int messageLength, boolean isDTLS) throws IOException {
+
+ this.isDTLS = isDTLS;
+
protocolVersion = ProtocolVersion.valueOf(s.getInt8(), s.getInt8());
clnt_random = new RandomCookie(s);
sessionId = new SessionId(s.getBytes8());
sessionId.checkLength(protocolVersion);
+ if (isDTLS) {
+ cookie = s.getBytes8();
+ } else {
+ cookie = null;
+ }
+
cipherSuites = new CipherSuiteList(s);
compression_methods = s.getBytes8();
if (messageLength() != messageLength) {
@@ -279,6 +371,28 @@
extensions.add(signatureAlgorithm);
}
+ void addMFLExtension(int maximumPacketSize) {
+ HelloExtension maxFragmentLength =
+ new MaxFragmentLengthExtension(maximumPacketSize);
+ extensions.add(maxFragmentLength);
+ }
+
+ void updateHelloCookie(MessageDigest cookieDigest) {
+ //
+ // Just use HandshakeOutStream to compute the hello verify cookie.
+ // Not actually used to output handshake message records.
+ //
+ HandshakeOutStream hos = new HandshakeOutStream(null);
+
+ try {
+ send(hos, false); // Do not count hello verify cookie.
+ } catch (IOException ioe) {
+ // unlikely to happen
+ }
+
+ cookieDigest.update(hos.toByteArray());
+ }
+
@Override
int messageType() { return ht_client_hello; }
@@ -290,6 +404,7 @@
*/
return (2 + 32 + 1 + 2 + 1
+ sessionId.length() /* ... + variable parts */
+ + (isDTLS ? (1 + cookie.length) : 0)
+ (cipherSuites.size() * 2)
+ compression_methods.length)
+ extensions.length();
@@ -297,13 +412,7 @@
@Override
void send(HandshakeOutStream s) throws IOException {
- s.putInt8(protocolVersion.major);
- s.putInt8(protocolVersion.minor);
- clnt_random.send(s);
- s.putBytes8(sessionId.getId());
- cipherSuites.send(s);
- s.putBytes8(compression_methods);
- extensions.send(s);
+ send(s, true); // Count hello verify cookie.
}
@Override
@@ -317,6 +426,10 @@
s.print("Session ID: ");
s.println(sessionId);
+ if (isDTLS) {
+ Debug.println(s, "cookie", cookie);
+ }
+
s.println("Cipher Suites: " + cipherSuites);
Debug.println(s, "Compression Methods", compression_methods);
@@ -324,6 +437,21 @@
s.println("***");
}
}
+
+ private void send(HandshakeOutStream s,
+ boolean computeCookie) throws IOException {
+ s.putInt8(protocolVersion.major);
+ s.putInt8(protocolVersion.minor);
+ clnt_random.send(s);
+ s.putBytes8(sessionId.getId());
+ if (isDTLS && computeCookie) {
+ s.putBytes8(cookie);
+ }
+ cipherSuites.send(s);
+ s.putBytes8(compression_methods);
+ extensions.send(s);
+ }
+
}
/*
@@ -740,7 +868,7 @@
setValues(obj);
Signature sig;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
this.preferableSignatureAlgorithm = signAlgorithm;
sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
} else {
@@ -801,7 +929,7 @@
new BigInteger(1, dh_g)));
// read the signature and hash algorithm
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
int hash = input.getInt8(); // hash algorithm
int signature = input.getInt8(); // signature algorithm
@@ -834,7 +962,7 @@
Signature sig;
String algorithm = publicKey.getAlgorithm();
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
sig = JsseJce.getSignature(
preferableSignatureAlgorithm.getAlgorithmName());
} else {
@@ -914,7 +1042,7 @@
temp += dh_Ys.length;
if (signature != null) {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
temp += SignatureAndHashAlgorithm.sizeInRecord();
}
@@ -934,7 +1062,7 @@
s.putBytes16(dh_Ys);
if (signature != null) {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
s.putInt8(preferableSignatureAlgorithm.getHashValue());
s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
}
@@ -959,7 +1087,7 @@
if (signature == null) {
s.println("Anonymous");
} else {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
s.println("Signature Algorithm " +
preferableSignatureAlgorithm.getAlgorithmName());
}
@@ -1021,7 +1149,7 @@
}
Signature sig;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
this.preferableSignatureAlgorithm = signAlgorithm;
sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
} else {
@@ -1084,7 +1212,7 @@
}
// read the signature and hash algorithm
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
int hash = input.getInt8(); // hash algorithm
int signature = input.getInt8(); // signature algorithm
@@ -1105,7 +1233,7 @@
// verify the signature
Signature sig;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
sig = JsseJce.getSignature(
preferableSignatureAlgorithm.getAlgorithmName());
} else {
@@ -1157,7 +1285,7 @@
int sigLen = 0;
if (signatureBytes != null) {
sigLen = 2 + signatureBytes.length;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
sigLen += SignatureAndHashAlgorithm.sizeInRecord();
}
}
@@ -1172,7 +1300,7 @@
s.putBytes8(pointBytes);
if (signatureBytes != null) {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
s.putInt8(preferableSignatureAlgorithm.getHashValue());
s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
}
@@ -1189,7 +1317,7 @@
if (signatureBytes == null) {
s.println("Anonymous");
} else {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
s.println("Signature Algorithm " +
preferableSignatureAlgorithm.getAlgorithmName());
}
@@ -1315,7 +1443,7 @@
this.types = JsseJce.isEcAvailable() ? TYPES_ECC : TYPES_NO_ECC;
// Use supported_signature_algorithms for TLS 1.2 or later.
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
if (signAlgs == null || signAlgs.isEmpty()) {
throw new SSLProtocolException(
"No supported signature algorithms");
@@ -1339,7 +1467,7 @@
types = input.getBytes8();
// Read the supported_signature_algorithms for TLS 1.2 or later.
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
algorithmsLen = input.getInt16();
if (algorithmsLen < 2) {
throw new SSLProtocolException(
@@ -1406,7 +1534,7 @@
int messageLength() {
int len = 1 + types.length + 2;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
len += algorithmsLen + 2;
}
@@ -1423,7 +1551,7 @@
output.putBytes8(types);
// put supported_signature_algorithms
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
output.putInt16(algorithmsLen);
for (SignatureAndHashAlgorithm algorithm : algorithms) {
output.putInt8(algorithm.getHashValue()); // hash
@@ -1478,7 +1606,7 @@
}
s.println();
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
StringBuilder sb = new StringBuilder();
boolean opened = false;
for (SignatureAndHashAlgorithm signAlg : algorithms) {
@@ -1576,7 +1704,7 @@
String algorithm = privateKey.getAlgorithm();
Signature sig = null;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
this.preferableSignatureAlgorithm = signAlgorithm;
sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
} else {
@@ -1598,7 +1726,7 @@
this.protocolVersion = protocolVersion;
// read the signature and hash algorithm
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
int hashAlg = input.getInt8(); // hash algorithm
int signAlg = input.getInt8(); // signature algorithm
@@ -1634,7 +1762,7 @@
SecretKey masterSecret) throws GeneralSecurityException {
String algorithm = publicKey.getAlgorithm();
Signature sig = null;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
sig = JsseJce.getSignature(
preferableSignatureAlgorithm.getAlgorithmName());
} else {
@@ -1676,11 +1804,11 @@
throws SignatureException {
if (algorithm.equals("RSA")) {
- if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1-
+ if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1-
MessageDigest md5Clone = handshakeHash.getMD5Clone();
MessageDigest shaClone = handshakeHash.getSHAClone();
- if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3
+ if (!protocolVersion.useTLS10PlusSpec()) { // SSLv3
updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey);
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
}
@@ -1692,10 +1820,10 @@
sig.update(handshakeHash.getAllHandshakeMessages());
}
} else { // DSA, ECDSA
- if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1-
+ if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1-
MessageDigest shaClone = handshakeHash.getSHAClone();
- if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3
+ if (!protocolVersion.useTLS10PlusSpec()) { // SSLv3
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
}
@@ -1811,7 +1939,7 @@
int messageLength() {
int temp = 2;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
temp += SignatureAndHashAlgorithm.sizeInRecord();
}
@@ -1820,7 +1948,7 @@
@Override
void send(HandshakeOutStream s) throws IOException {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
s.putInt8(preferableSignatureAlgorithm.getHashValue());
s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
}
@@ -1833,7 +1961,7 @@
s.println("*** CertificateVerify");
if (debug != null && Debug.isOn("verbose")) {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
s.println("Signature Algorithm " +
preferableSignatureAlgorithm.getAlgorithmName());
}
@@ -1899,7 +2027,7 @@
CipherSuite cipherSuite) throws IOException {
this.protocolVersion = protocolVersion;
this.cipherSuite = cipherSuite;
- int msgLen = (protocolVersion.v >= ProtocolVersion.TLS10.v) ? 12 : 36;
+ int msgLen = protocolVersion.useTLS10PlusSpec() ? 12 : 36;
verifyData = new byte[msgLen];
input.read(verifyData);
}
@@ -1932,7 +2060,7 @@
throw new RuntimeException("Invalid sender: " + sender);
}
- if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+ if (protocolVersion.useTLS10PlusSpec()) {
// TLS 1.0+
try {
byte [] seed;
@@ -1940,14 +2068,14 @@
PRF prf;
// Get the KeyGenerator alg and calculate the seed.
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
- // TLS 1.2
+ if (protocolVersion.useTLS12PlusSpec()) {
+ // TLS 1.2+ or DTLS 1.2+
seed = handshakeHash.getFinishedHash();
prfAlg = "SunTls12Prf";
prf = cipherSuite.prfAlg;
} else {
- // TLS 1.0/1.1
+ // TLS 1.0/1.1, DTLS 1.0
MessageDigest md5Clone = handshakeHash.getMD5Clone();
MessageDigest shaClone = handshakeHash.getSHAClone();
seed = new byte[36];
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java Thu Jun 04 18:49:37 2015 -0700
@@ -23,10 +23,9 @@
* questions.
*/
-
package sun.security.ssl;
-import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
@@ -40,197 +39,113 @@
*
* @author David Brownell
*/
-public class HandshakeOutStream extends OutputStream {
-
- private SSLSocketImpl socket;
- private SSLEngineImpl engine;
-
- OutputRecord r;
+public class HandshakeOutStream extends ByteArrayOutputStream {
- HandshakeOutStream(ProtocolVersion protocolVersion,
- ProtocolVersion helloVersion, HandshakeHash handshakeHash,
- SSLSocketImpl socket) {
- this.socket = socket;
- r = new OutputRecord(Record.ct_handshake);
- init(protocolVersion, helloVersion, handshakeHash);
- }
+ OutputRecord outputRecord; // May be null if not actually used to
+ // output handshake message records.
- HandshakeOutStream(ProtocolVersion protocolVersion,
- ProtocolVersion helloVersion, HandshakeHash handshakeHash,
- SSLEngineImpl engine) {
- this.engine = engine;
- r = new EngineOutputRecord(Record.ct_handshake, engine);
- init(protocolVersion, helloVersion, handshakeHash);
- }
-
- private void init(ProtocolVersion protocolVersion,
- ProtocolVersion helloVersion, HandshakeHash handshakeHash) {
- r.setVersion(protocolVersion);
- r.setHelloVersion(helloVersion);
- r.setHandshakeHash(handshakeHash);
+ HandshakeOutStream(OutputRecord outputRecord) {
+ super();
+ this.outputRecord = outputRecord;
}
+ // Complete a handshakin message writing. Called by HandshakeMessage.
+ void complete() throws IOException {
+ if (size() < 4) { // 4: handshake message header size
+ // internal_error alert will be triggered
+ throw new RuntimeException("handshake message is not available");
+ }
- /*
- * Update the handshake data hashes ... mostly for use after a
- * client cert has been sent, so the cert verify message can be
- * constructed correctly yet without forcing extra I/O. In all
- * other cases, automatic hash calculation suffices.
- */
- void doHashes() {
- r.doHashes();
+ // outputRecord cannot be null
+ outputRecord.encodeHandshake(buf, 0, count);
+
+ // reset the byte array output stream
+ reset();
}
- /*
- * Write some data out onto the stream ... buffers as much as possible.
- * Hashes are updated automatically if something gets flushed to the
- * network (e.g. a big cert message etc).
- */
- @Override
- public void write(byte buf[], int off, int len) throws IOException {
- while (len > 0) {
- int howmuch = Math.min(len, r.availableDataBytes());
+ //
+ // overridden ByteArrayOutputStream methods
+ //
- if (howmuch == 0) {
- flush();
- } else {
- r.write(buf, off, howmuch);
- off += howmuch;
- len -= howmuch;
- }
- }
- }
-
- /*
- * write-a-byte
- */
@Override
- public void write(int i) throws IOException {
- if (r.availableDataBytes() < 1) {
- flush();
- }
- r.write(i);
+ public void write(byte[] b, int off, int len) {
+ // The maximum fragment size is 24 bytes.
+ checkOverflow(len, Record.OVERFLOW_OF_INT24);
+ super.write(b, off, len);
}
@Override
public void flush() throws IOException {
- if (socket != null) {
- try {
- socket.writeRecord(r);
- } catch (IOException e) {
- // Had problems writing; check if there was an
- // alert from peer. If alert received, waitForClose
- // will throw an exception for the alert
- socket.waitForClose(true);
-
- // No alert was received, just rethrow exception
- throw e;
- }
- } else { // engine != null
- /*
- * Even if record might be empty, flush anyway in case
- * there is a finished handshake message that we need
- * to queue.
- */
- engine.writeRecord((EngineOutputRecord)r);
- }
+ outputRecord.flush();
}
- /*
- * Tell the OutputRecord that a finished message was
- * contained either in this record or the one immeiately
- * preceding it. We need to reliably pass back notifications
- * that a finish message occurred.
- */
- void setFinishedMsg() {
- assert(socket == null);
-
- ((EngineOutputRecord)r).setFinishedMsg();
- }
+ //
+ // handshake output stream management functions
+ //
/*
* Put integers encoded in standard 8, 16, 24, and 32 bit
* big endian formats. Note that OutputStream.write(int) only
* writes the least significant 8 bits and ignores the rest.
*/
-
void putInt8(int i) throws IOException {
checkOverflow(i, Record.OVERFLOW_OF_INT08);
- r.write(i);
+ super.write(i);
}
void putInt16(int i) throws IOException {
checkOverflow(i, Record.OVERFLOW_OF_INT16);
- if (r.availableDataBytes() < 2) {
- flush();
- }
- r.write(i >> 8);
- r.write(i);
+ super.write(i >> 8);
+ super.write(i);
}
void putInt24(int i) throws IOException {
checkOverflow(i, Record.OVERFLOW_OF_INT24);
- if (r.availableDataBytes() < 3) {
- flush();
- }
- r.write(i >> 16);
- r.write(i >> 8);
- r.write(i);
- }
-
- void putInt32(int i) throws IOException {
- if (r.availableDataBytes() < 4) {
- flush();
- }
- r.write(i >> 24);
- r.write(i >> 16);
- r.write(i >> 8);
- r.write(i);
+ super.write(i >> 16);
+ super.write(i >> 8);
+ super.write(i);
}
/*
* Put byte arrays with length encoded as 8, 16, 24 bit
* integers in big-endian format.
*/
- void putBytes8(byte b[]) throws IOException {
+ void putBytes8(byte[] b) throws IOException {
if (b == null) {
putInt8(0);
- return;
} else {
- checkOverflow(b.length, Record.OVERFLOW_OF_INT08);
+ putInt8(b.length);
+ super.write(b, 0, b.length);
}
- putInt8(b.length);
- write(b, 0, b.length);
}
public void putBytes16(byte b[]) throws IOException {
if (b == null) {
putInt16(0);
- return;
} else {
- checkOverflow(b.length, Record.OVERFLOW_OF_INT16);
+ putInt16(b.length);
+ super.write(b, 0, b.length);
}
- putInt16(b.length);
- write(b, 0, b.length);
}
void putBytes24(byte b[]) throws IOException {
if (b == null) {
putInt24(0);
- return;
} else {
- checkOverflow(b.length, Record.OVERFLOW_OF_INT24);
+ putInt24(b.length);
+ super.write(b, 0, b.length);
}
- putInt24(b.length);
- write(b, 0, b.length);
}
- private void checkOverflow(int length, int overflow) {
- if (length >= overflow) {
+ /*
+ * Does the specified length overflow the limitation?
+ */
+ private static void checkOverflow(int length, int limit) {
+ if (length >= limit) {
// internal_error alert will be triggered
throw new RuntimeException(
"Field length overflow, the field length (" +
- length + ") should be less than " + overflow);
+ length + ") should be less than " + limit);
}
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeStateManager.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,925 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.util.LinkedList;
+import java.util.HashMap;
+import javax.net.ssl.SSLProtocolException;
+
+import sun.security.ssl.HandshakeMessage.*;
+
+import static sun.security.ssl.CipherSuite.KeyExchange;
+import static sun.security.ssl.CipherSuite.KeyExchange.*;
+import static sun.security.ssl.HandshakeStateManager.HandshakeState.*;
+import static sun.security.ssl.HandshakeMessage.*;
+
+/*
+ * Handshake state manager.
+ *
+ * Messages flow for a full handshake:
+ *
+ * - -
+ * | HelloRequest (No.0, RFC 5246) [*] |
+ * | <-------------------------------------------- |
+ * | |
+ * | ClientHello (No.1, RFC 5246) |
+ * | --------------------------------------------> |
+ * | |
+ * | - HelloVerifyRequest (No.3, RFC 6347) - |
+ * | D | <-------------------------------------------- | D |
+ * | T | | T |
+ * | L | ClientHello (No.1, RFC 5246) | L |
+ * | S | --------------------------------------------> | S |
+ * | - - |
+ * | |
+ * C | ServerHello (No.2, RFC 5246) | S
+ * L | SupplementalData (No.23, RFC4680) [*] | E
+ * I | Certificate (No.11, RFC 5246) [*] | R
+ * E | CertificateStatus (No.22, RFC 6066) [*] | V
+ * N | ServerKeyExchange (No.12, RFC 5246) [*] | E
+ * T | CertificateRequest (No.13, RFC 5246) [*] | R
+ * | ServerHelloDone (No.14, RFC 5246) |
+ * | <-------------------------------------------- |
+ * | |
+ * | SupplementalData (No.23, RFC4680) [*] |
+ * | Certificate (No.11, RFC 5246) [*] Or |
+ * | CertificateURL (No.21, RFC6066) [*] |
+ * | ClientKeyExchange (No.16, RFC 5246) |
+ * | CertificateVerify (No.15, RFC 5246) [*] |
+ * | [ChangeCipherSpec] (RFC 5246) |
+ * | Finished (No.20, RFC 5246) |
+ * | --------------------------------------------> |
+ * | |
+ * | NewSessionTicket (No.4, RFC4507) [*] |
+ * | [ChangeCipherSpec] (RFC 5246) |
+ * | Finished (No.20, RFC 5246) |
+ * | <-------------------------------------------- |
+ * - -
+ * [*] Indicates optional or situation-dependent messages that are not
+ * always sent.
+ *
+ * Message flow for an abbreviated handshake:
+ * - -
+ * | ClientHello (No.1, RFC 5246) |
+ * | --------------------------------------------> |
+ * | |
+ * C | ServerHello (No.2, RFC 5246) | S
+ * L | NewSessionTicket (No.4, RFC4507) [*] | E
+ * I | [ChangeCipherSpec] (RFC 5246) | R
+ * E | Finished (No.20, RFC 5246) | V
+ * N | <-------------------------------------------- | E
+ * T | | R
+ * | [ChangeCipherSpec] (RFC 5246) |
+ * | Finished (No.20, RFC 5246) |
+ * | --------------------------------------------> |
+ * - -
+ *
+ *
+ * State machine of handshake states:
+ *
+ * +--------------+
+ * START -----> | HelloRequest |
+ * | +--------------+
+ * | |
+ * v v
+ * +---------------------+ --> +---------------------+
+ * | ClientHello | | HelloVerifyRequest |
+ * +---------------------+ <-- +---------------------+
+ * |
+ * |
+ * =========================================================================
+ * |
+ * v
+ * +---------------------+
+ * | ServerHello | ----------------------------------+------+
+ * +---------------------+ --> +-------------------------+ | |
+ * | | Server SupplementalData | | |
+ * | +-------------------------+ | |
+ * | | | |
+ * v v | |
+ * +---------------------+ | |
+ * +---- | Server Certificate | | |
+ * | +---------------------+ | |
+ * | | | |
+ * | | +--------------------+ | |
+ * | +-> | CertificateStatus | | |
+ * | | +--------------------+ v |
+ * | | | | +--------------------+ |
+ * | v v +--> | ServerKeyExchange | |
+ * | +---------------------+ | +--------------------+ |
+ * | | CertificateRequest | | | |
+ * | +---------------------+ <-+---------+ |
+ * | | | | |
+ * v v | | |
+ * +---------------------+ <-------+ | |
+ * | ServerHelloDone | <-----------------+ |
+ * +---------------------+ |
+ * | | |
+ * | | |
+ * | | |
+ * =========================================================================
+ * | | |
+ * | v |
+ * | +-------------------------+ |
+ * | | Client SupplementalData | --------------+ |
+ * | +-------------------------+ | |
+ * | | | |
+ * | v | |
+ * | +--------------------+ | |
+ * +-> | Client Certificate | ALT. | |
+ * | +--------------------+----------------+ | |
+ * | | CertificateURL | | |
+ * | +----------------+ | |
+ * v | |
+ * +-------------------+ <------------------------+ |
+ * | ClientKeyExchange | |
+ * +-------------------+ |
+ * | | |
+ * | v |
+ * | +-------------------+ |
+ * | | CertificateVerify | |
+ * | +-------------------+ |
+ * | | |
+ * v v |
+ * +-------------------------+ |
+ * | Client ChangeCipherSpec | <---------------+ |
+ * +-------------------------+ | |
+ * | | |
+ * v | |
+ * +-----------------+ (abbreviated) | |
+ * | Client Finished | -------------> END | |
+ * +-----------------+ (Abbreviated handshake) | |
+ * | | |
+ * | (full) | |
+ * | | |
+ * ================================ | |
+ * | | |
+ * | ================================
+ * | | |
+ * v | |
+ * +------------------+ | (abbreviated) |
+ * | NewSessionTicket | <--------------------------------+
+ * +------------------+ | |
+ * | | |
+ * v | |
+ * +-------------------------+ | (abbreviated) |
+ * | Server ChangeCipherSpec | <-------------------------------------+
+ * +-------------------------+ |
+ * | |
+ * v |
+ * +-----------------+ (abbreviated) |
+ * | Server Finished | -------------------------+
+ * +-----------------+
+ * | (full)
+ * v
+ * END (Full handshake)
+ *
+ *
+ * The scenarios of the use of this class:
+ * 1. Create an instance of HandshakeStateManager during the initializtion
+ * handshake.
+ * 2. If receiving a handshake message, call HandshakeStateManager.check()
+ * to make sure that the message is of the expected handshake type. And
+ * then call HandshakeStateManager.update() in case handshake states may
+ * be impacted by this new incoming handshake message.
+ * 3. On delivering a handshake message, call HandshakeStateManager.update()
+ * in case handshake states may by thie new outgoing handshake message.
+ * 4. On receiving and delivering ChangeCipherSpec message, call
+ * HandshakeStateManager.changeCipherSpec() to check the present sequence
+ * of this message, and update the states if necessary.
+ */
+final class HandshakeStateManager {
+ // upcoming handshake states.
+ private LinkedList<HandshakeState> upcomingStates;
+ private LinkedList<HandshakeState> alternatives;
+
+ private boolean isDTLS;
+
+ private final static boolean debugIsOn;
+
+ private final static HashMap<Byte, String> handshakeTypes;
+
+ static {
+ debugIsOn = (Handshaker.debug != null) &&
+ Debug.isOn("handshake") && Debug.isOn("verbose");
+ handshakeTypes = new HashMap<>(15);
+
+ handshakeTypes.put(ht_hello_request, "hello_request");
+ handshakeTypes.put(ht_client_hello, "client_hello");
+ handshakeTypes.put(ht_server_hello, "server_hello");
+ handshakeTypes.put(ht_hello_verify_request, "hello_verify_request");
+ handshakeTypes.put(ht_new_session_ticket, "session_ticket");
+ handshakeTypes.put(ht_certificate, "certificate");
+ handshakeTypes.put(ht_server_key_exchange, "server_key_exchange");
+ handshakeTypes.put(ht_certificate_request, "certificate_request");
+ handshakeTypes.put(ht_server_hello_done, "server_hello_done");
+ handshakeTypes.put(ht_certificate_verify, "certificate_verify");
+ handshakeTypes.put(ht_client_key_exchange, "client_key_exchange");
+ handshakeTypes.put(ht_finished, "finished");
+ handshakeTypes.put(ht_certificate_url, "certificate_url");
+ handshakeTypes.put(ht_certificate_status, "certificate_status");
+ handshakeTypes.put(ht_supplemental_data, "supplemental_data");
+ }
+
+ HandshakeStateManager(boolean isDTLS) {
+ this.upcomingStates = new LinkedList<>();
+ this.alternatives = new LinkedList<>();
+ this.isDTLS = isDTLS;
+ }
+
+ //
+ // enumation of handshake type
+ //
+ static enum HandshakeState {
+ HS_HELLO_REQUEST(
+ "hello_request",
+ HandshakeMessage.ht_hello_request),
+ HS_CLIENT_HELLO(
+ "client_hello",
+ HandshakeMessage.ht_client_hello),
+ HS_HELLO_VERIFY_REQUEST(
+ "hello_verify_request",
+ HandshakeMessage.ht_hello_verify_request),
+ HS_SERVER_HELLO(
+ "server_hello",
+ HandshakeMessage.ht_server_hello),
+ HS_SERVER_SUPPLEMENTAL_DATA(
+ "server supplemental_data",
+ HandshakeMessage.ht_supplemental_data, true),
+ HS_SERVER_CERTIFICATE(
+ "server certificate",
+ HandshakeMessage.ht_certificate),
+ HS_CERTIFICATE_STATUS(
+ "certificate_status",
+ HandshakeMessage.ht_certificate_status, true),
+ HS_SERVER_KEY_EXCHANGE(
+ "server_key_exchange",
+ HandshakeMessage.ht_server_key_exchange, true),
+ HS_CERTIFICATE_REQUEST(
+ "certificate_request",
+ HandshakeMessage.ht_certificate_request, true),
+ HS_SERVER_HELLO_DONE(
+ "server_hello_done",
+ HandshakeMessage.ht_server_hello_done),
+ HS_CLIENT_SUPPLEMENTAL_DATA(
+ "client supplemental_data",
+ HandshakeMessage.ht_supplemental_data, true),
+ HS_CLIENT_CERTIFICATE(
+ "client certificate",
+ HandshakeMessage.ht_certificate, true),
+ HS_CERTIFICATE_URL(
+ "certificate_url",
+ HandshakeMessage.ht_certificate_url, true),
+ HS_CLIENT_KEY_EXCHANGE(
+ "client_key_exchange",
+ HandshakeMessage.ht_client_key_exchange),
+ HS_CERTIFICATE_VERIFY(
+ "certificate_verify",
+ HandshakeMessage.ht_certificate_verify, true),
+ HS_CLIENT_CHANGE_CIPHER_SPEC(
+ "client change_cipher_spec",
+ HandshakeMessage.ht_not_applicable),
+ HS_CLEINT_FINISHED(
+ "client finished",
+ HandshakeMessage.ht_finished),
+ HS_NEW_SESSION_TICKET(
+ "session_ticket",
+ HandshakeMessage.ht_new_session_ticket),
+ HS_SERVER_CHANGE_CIPHER_SPEC(
+ "server change_cipher_spec",
+ HandshakeMessage.ht_not_applicable),
+ HS_SERVER_FINISHDE(
+ "server finished",
+ HandshakeMessage.ht_finished);
+
+ final String description;
+ final byte handshakeType;
+ final boolean isOptional;
+
+ HandshakeState(String description, byte handshakeType) {
+ this.description = description;
+ this.handshakeType = handshakeType;
+ this.isOptional = false;
+ }
+
+ HandshakeState(String description,
+ byte handshakeType, boolean isOptional) {
+
+ this.description = description;
+ this.handshakeType = handshakeType;
+ this.isOptional = isOptional;
+ }
+
+ public String toString() {
+ return description + "[" + handshakeType + "]" +
+ (isOptional ? "(optional)" : "");
+ }
+ }
+
+ boolean isEmpty() {
+ return upcomingStates.isEmpty();
+ }
+
+ void check(byte handshakeType) throws SSLProtocolException {
+ String exceptionMsg =
+ "Handshake message sequence violation, " + handshakeType;
+
+ if (debugIsOn) {
+ System.out.println(
+ "check handshake state: " + toString(handshakeType));
+ }
+
+ if (upcomingStates.isEmpty()) {
+ // Is it a kickstart message?
+ if ((handshakeType != HandshakeMessage.ht_hello_request) &&
+ (handshakeType != HandshakeMessage.ht_client_hello)) {
+
+ throw new SSLProtocolException(
+ "Handshake message sequence violation, " + handshakeType);
+ }
+
+ // It is a kickstart message.
+ return;
+ }
+
+ // Ignore the checking for HelloRequest messages as they are
+ // may be sent by the server at any time.
+ if (handshakeType == HandshakeMessage.ht_hello_request) {
+ return;
+ }
+
+ for (HandshakeState handshakeState : upcomingStates) {
+ if (handshakeState.handshakeType == handshakeType) {
+ // It's the expected next handshake type.
+ return;
+ }
+
+ if (handshakeState.isOptional) {
+ continue;
+ } else {
+ for (HandshakeState alternative : alternatives) {
+ if (alternative.handshakeType == handshakeType) {
+ return;
+ }
+
+ if (alternative.isOptional) {
+ continue;
+ } else {
+ throw new SSLProtocolException(exceptionMsg);
+ }
+ }
+ }
+
+ throw new SSLProtocolException(exceptionMsg);
+ }
+
+ // Not an expected Handshake message.
+ throw new SSLProtocolException(
+ "Handshake message sequence violation, " + handshakeType);
+ }
+
+ void update(HandshakeMessage handshakeMessage,
+ boolean isAbbreviated) throws SSLProtocolException {
+
+ byte handshakeType = (byte)handshakeMessage.messageType();
+ String exceptionMsg =
+ "Handshake message sequence violation, " + handshakeType;
+
+ if (debugIsOn) {
+ System.out.println(
+ "update handshake state: " + toString(handshakeType));
+ }
+
+ boolean hasPresentState = false;
+ switch (handshakeType) {
+ case HandshakeMessage.ht_hello_request:
+ //
+ // State machine:
+ // PRESENT: START
+ // TO : ClientHello
+ //
+
+ // No old state to update.
+
+ // Add the upcoming states.
+ if (!upcomingStates.isEmpty()) {
+ // A ClientHello message should be followed.
+ upcomingStates.add(HS_CLIENT_HELLO);
+
+ } // Otherwise, ignore this HelloRequest message.
+
+ break;
+
+ case HandshakeMessage.ht_client_hello:
+ //
+ // State machine:
+ // PRESENT: START
+ // HS_CLIENT_HELLO
+ // TO : HS_HELLO_VERIFY_REQUEST (DTLS)
+ // HS_SERVER_HELLO
+ //
+
+ // Check and update the present state.
+ if (!upcomingStates.isEmpty()) {
+ // The current state should be HS_CLIENT_HELLO.
+ HandshakeState handshakeState = upcomingStates.pop();
+ if (handshakeState != HS_CLIENT_HELLO) {
+ throw new SSLProtocolException(exceptionMsg);
+ }
+ }
+
+ // Add the upcoming states.
+ ClientHello clientHello = (ClientHello)handshakeMessage;
+ if (isDTLS) {
+ // Is it an initial ClientHello message?
+ if (clientHello.cookie == null ||
+ clientHello.cookie.length == 0) {
+ // Is it an abbreviated handshake?
+ if (clientHello.sessionId.length() != 0) {
+ // A HelloVerifyRequest message or a ServerHello
+ // message may follow the abbreviated session
+ // resuming handshake request.
+ upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
+ alternatives.add(HS_SERVER_HELLO);
+ } else {
+ // A HelloVerifyRequest message should follow
+ // the initial ClientHello message.
+ upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
+ }
+ } else {
+ // A HelloVerifyRequest may be followed if the cookie
+ // cannot be verified.
+ upcomingStates.add(HS_SERVER_HELLO);
+ alternatives.add(HS_HELLO_VERIFY_REQUEST);
+ }
+ } else {
+ upcomingStates.add(HS_SERVER_HELLO);
+ }
+
+ break;
+
+ case HandshakeMessage.ht_hello_verify_request:
+ //
+ // State machine:
+ // PRESENT: HS_HELLO_VERIFY_REQUEST
+ // TO : HS_CLIENT_HELLO
+ //
+ // Note that this state may have an alternative option.
+
+ // Check and update the present state.
+ if (!upcomingStates.isEmpty()) {
+ // The current state should be HS_HELLO_VERIFY_REQUEST.
+ HandshakeState handshakeState = upcomingStates.pop();
+ HandshakeState alternative = null;
+ if (!alternatives.isEmpty()) {
+ alternative = alternatives.pop();
+ }
+
+ if ((handshakeState != HS_HELLO_VERIFY_REQUEST) &&
+ (alternative != HS_HELLO_VERIFY_REQUEST)) {
+
+ throw new SSLProtocolException(exceptionMsg);
+ }
+ } else {
+ // No present state.
+ throw new SSLProtocolException(exceptionMsg);
+ }
+
+ // Add the upcoming states.
+ upcomingStates.add(HS_CLIENT_HELLO);
+
+ break;
+
+ case HandshakeMessage.ht_server_hello:
+ //
+ // State machine:
+ // PRESENT: HS_SERVER_HELLO
+ // TO :
+ // Full handshake state stacks
+ // (ServerHello Flight)
+ // HS_SERVER_SUPPLEMENTAL_DATA [optional]
+ // --> HS_SERVER_CERTIFICATE [optional]
+ // --> HS_CERTIFICATE_STATUS [optional]
+ // --> HS_SERVER_KEY_EXCHANGE [optional]
+ // --> HS_CERTIFICATE_REQUEST [optional]
+ // --> HS_SERVER_HELLO_DONE
+ // (Client ClientKeyExchange Flight)
+ // --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
+ // --> HS_CLIENT_CERTIFICATE or
+ // HS_CERTIFICATE_URL
+ // --> HS_CLIENT_KEY_EXCHANGE
+ // --> HS_CERTIFICATE_VERIFY [optional]
+ // --> HS_CLIENT_CHANGE_CIPHER_SPEC
+ // --> HS_CLEINT_FINISHED
+ // (Server Finished Flight)
+ // --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
+ //
+ // Abbreviated handshake state stacks
+ // (Server Finished Flight)
+ // HS_NEW_SESSION_TICKET
+ // --> HS_SERVER_CHANGE_CIPHER_SPEC
+ // --> HS_SERVER_FINISHDE
+ // (Client Finished Flight)
+ // --> HS_CLIENT_CHANGE_CIPHER_SPEC
+ // --> HS_CLEINT_FINISHED
+ //
+ // Note that this state may have an alternative option.
+
+ // Check and update the present state.
+ if (!upcomingStates.isEmpty()) {
+ // The current state should be HS_SERVER_HELLO
+ HandshakeState handshakeState = upcomingStates.pop();
+ HandshakeState alternative = null;
+ if (!alternatives.isEmpty()) {
+ alternative = alternatives.pop();
+ }
+
+ if ((handshakeState != HS_SERVER_HELLO) &&
+ (alternative != HS_SERVER_HELLO)) {
+
+ throw new SSLProtocolException(exceptionMsg);
+ }
+ } else {
+ // No present state.
+ throw new SSLProtocolException(exceptionMsg);
+ }
+
+ // Add the upcoming states.
+ ServerHello serverHello = (ServerHello)handshakeMessage;
+ HelloExtensions hes = serverHello.extensions;
+
+
+ // Not support SessionTicket extension yet.
+ //
+ // boolean hasSessionTicketExt =
+ // (hes.get(HandshakeMessage.ht_new_session_ticket) != null);
+
+ if (isAbbreviated) {
+ // Not support SessionTicket extension yet.
+ //
+ // // Mandatory NewSessionTicket message
+ // if (hasSessionTicketExt) {
+ // upcomingStates.add(HS_NEW_SESSION_TICKET);
+ // }
+
+ // Mandatory server ChangeCipherSpec and Finished messages
+ upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
+ upcomingStates.add(HS_SERVER_FINISHDE);
+
+ // Mandatory client ChangeCipherSpec and Finished messages
+ upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
+ upcomingStates.add(HS_CLEINT_FINISHED);
+ } else {
+ // Not support SupplementalData extension yet.
+ //
+ // boolean hasSupplementalDataExt =
+ // (hes.get(HandshakeMessage.ht_supplemental_data) != null);
+
+ // Not support CertificateStatus extension yet.
+ //
+ // boolean hasCertificateStatusExt =
+ // (hes.get(HandshakeMessage.ht_certificate_status) != null);
+
+ // Not support CertificateURL extension yet.
+ //
+ // boolean hasCertificateUrlExt =
+ // (hes.get(HandshakeMessage.ht_certificate_url) != null);
+
+ // Not support SupplementalData extension yet.
+ //
+ // // Optional SupplementalData message
+ // if (hasSupplementalDataExt) {
+ // upcomingStates.add(HS_SERVER_SUPPLEMENTAL_DATA);
+ // }
+
+ // Need server Certificate message or not?
+ KeyExchange keyExchange = serverHello.cipherSuite.keyExchange;
+ if ((keyExchange != K_KRB5) &&
+ (keyExchange != K_KRB5_EXPORT) &&
+ (keyExchange != K_DH_ANON) &&
+ (keyExchange != K_ECDH_ANON)) {
+ // Mandatory Certificate message
+ upcomingStates.add(HS_SERVER_CERTIFICATE);
+ }
+
+ // Not support CertificateStatus extension yet.
+ //
+ // // Optional CertificateStatus message
+ // if (hasCertificateStatusExt) {
+ // upcomingStates.add(HS_CERTIFICATE_STATUS);
+ // }
+
+ // Need ServerKeyExchange message or not?
+ if ((keyExchange == K_RSA_EXPORT) ||
+ (keyExchange == K_DHE_RSA) ||
+ (keyExchange == K_DHE_DSS) ||
+ (keyExchange == K_DH_ANON) ||
+ (keyExchange == K_ECDHE_RSA) ||
+ (keyExchange == K_ECDHE_ECDSA) ||
+ (keyExchange == K_ECDH_ANON)) {
+ // Optional ServerKeyExchange message
+ upcomingStates.add(HS_SERVER_KEY_EXCHANGE);
+ }
+
+ // Optional CertificateRequest message
+ upcomingStates.add(HS_CERTIFICATE_REQUEST);
+
+ // Mandatory ServerHelloDone message
+ upcomingStates.add(HS_SERVER_HELLO_DONE);
+
+ // Not support SupplementalData extension yet.
+ //
+ // // Optional SupplementalData message
+ // if (hasSupplementalDataExt) {
+ // upcomingStates.add(HS_CLIENT_SUPPLEMENTAL_DATA);
+ // }
+
+ // Optional client Certificate message
+ upcomingStates.add(HS_CLIENT_CERTIFICATE);
+
+ // Not support CertificateURL extension yet.
+ //
+ // // Alternative CertificateURL message, optional too.
+ // //
+ // // Please put CertificateURL rather than Certificate
+ // // message in the alternatives list. So that we can
+ // // simplify the process of this alternative pair later.
+ // if (hasCertificateUrlExt) {
+ // alternatives.add(HS_CERTIFICATE_URL);
+ // }
+
+ // Mandatory ClientKeyExchange message
+ upcomingStates.add(HS_CLIENT_KEY_EXCHANGE);
+
+ // Optional CertificateVerify message
+ upcomingStates.add(HS_CERTIFICATE_VERIFY);
+
+ // Mandatory client ChangeCipherSpec and Finished messages
+ upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
+ upcomingStates.add(HS_CLEINT_FINISHED);
+
+ // Not support SessionTicket extension yet.
+ //
+ // // Mandatory NewSessionTicket message
+ // if (hasSessionTicketExt) {
+ // upcomingStates.add(HS_NEW_SESSION_TICKET);
+ // }
+
+ // Mandatory server ChangeCipherSpec and Finished messages
+ upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
+ upcomingStates.add(HS_SERVER_FINISHDE);
+ }
+
+ break;
+
+ case HandshakeMessage.ht_certificate:
+ //
+ // State machine:
+ // PRESENT: HS_CERTIFICATE_URL or
+ // HS_CLIENT_CERTIFICATE
+ // TO : HS_CLIENT_KEY_EXCHANGE
+ //
+ // Or
+ //
+ // PRESENT: HS_SERVER_CERTIFICATE
+ // TO : HS_CERTIFICATE_STATUS [optional]
+ // HS_SERVER_KEY_EXCHANGE [optional]
+ // HS_CERTIFICATE_REQUEST [optional]
+ // HS_SERVER_HELLO_DONE
+ //
+ // Note that this state may have an alternative option.
+
+ // Check and update the present state.
+ while (!upcomingStates.isEmpty()) {
+ HandshakeState handshakeState = upcomingStates.pop();
+ if (handshakeState.handshakeType == handshakeType) {
+ hasPresentState = true;
+
+ // The current state should be HS_CLIENT_CERTIFICATE or
+ // HS_SERVER_CERTIFICATE.
+ //
+ // Note that we won't put HS_CLIENT_CERTIFICATE into
+ // the alternative list.
+ if ((handshakeState != HS_CLIENT_CERTIFICATE) &&
+ (handshakeState != HS_SERVER_CERTIFICATE)) {
+ throw new SSLProtocolException(exceptionMsg);
+ }
+
+ // Is it an expected client Certificate message?
+ boolean isClientMessage = false;
+ if (!upcomingStates.isEmpty()) {
+ // If the next expected message is ClientKeyExchange,
+ // this one should be an expected client Certificate
+ // message.
+ HandshakeState nextState = upcomingStates.getFirst();
+ if (nextState == HS_CLIENT_KEY_EXCHANGE) {
+ isClientMessage = true;
+ }
+ }
+
+ if (isClientMessage) {
+ if (handshakeState != HS_CLIENT_CERTIFICATE) {
+ throw new SSLProtocolException(exceptionMsg);
+ }
+
+ // Not support CertificateURL extension yet.
+ /*******************************************
+ // clear up the alternatives list
+ if (!alternatives.isEmpty()) {
+ HandshakeState alternative = alternatives.pop();
+
+ if (alternative != HS_CERTIFICATE_URL) {
+ throw new SSLProtocolException(exceptionMsg);
+ }
+ }
+ ********************************************/
+ } else {
+ if ((handshakeState != HS_SERVER_CERTIFICATE)) {
+ throw new SSLProtocolException(exceptionMsg);
+ }
+ }
+
+ break;
+ } else if (!handshakeState.isOptional) {
+ throw new SSLProtocolException(exceptionMsg);
+ } // Otherwise, looking for next state track.
+ }
+
+ // No present state.
+ if (!hasPresentState) {
+ throw new SSLProtocolException(exceptionMsg);
+ }
+
+ // no new upcoming states.
+
+ break;
+
+ // Not support CertificateURL extension yet.
+ /*************************************************/
+ case HandshakeMessage.ht_certificate_url:
+ //
+ // State machine:
+ // PRESENT: HS_CERTIFICATE_URL or
+ // HS_CLIENT_CERTIFICATE
+ // TO : HS_CLIENT_KEY_EXCHANGE
+ //
+ // Note that this state may have an alternative option.
+
+ // Check and update the present state.
+ while (!upcomingStates.isEmpty()) {
+ // The current state should be HS_CLIENT_CERTIFICATE.
+ //
+ // Note that we won't put HS_CLIENT_CERTIFICATE into
+ // the alternative list.
+ HandshakeState handshakeState = upcomingStates.pop();
+ if (handshakeState.handshakeType ==
+ HS_CLIENT_CERTIFICATE.handshakeType) {
+ hasPresentState = true;
+
+ // Look for HS_CERTIFICATE_URL state track.
+ if (!alternatives.isEmpty()) {
+ HandshakeState alternative = alternatives.pop();
+
+ if (alternative != HS_CERTIFICATE_URL) {
+ throw new SSLProtocolException(exceptionMsg);
+ }
+ } else {
+ // No alternative CertificateUR state track.
+ throw new SSLProtocolException(exceptionMsg);
+ }
+
+ if ((handshakeState != HS_CLIENT_CERTIFICATE)) {
+ throw new SSLProtocolException(exceptionMsg);
+ }
+
+ break;
+ } else if (!handshakeState.isOptional) {
+ throw new SSLProtocolException(exceptionMsg);
+ } // Otherwise, looking for next state track.
+
+ }
+
+ // No present state.
+ if (!hasPresentState) {
+ // No present state.
+ throw new SSLProtocolException(exceptionMsg);
+ }
+
+ // no new upcoming states.
+
+ break;
+ /*************************************************/
+
+ default:
+ // Check and update the present state.
+ while (!upcomingStates.isEmpty()) {
+ HandshakeState handshakeState = upcomingStates.pop();
+ if (handshakeState.handshakeType == handshakeType) {
+ hasPresentState = true;
+ break;
+ } else if (!handshakeState.isOptional) {
+ throw new SSLProtocolException(exceptionMsg);
+ } // Otherwise, looking for next state track.
+ }
+
+ // No present state.
+ if (!hasPresentState) {
+ throw new SSLProtocolException(exceptionMsg);
+ }
+
+ // no new upcoming states.
+ }
+
+ if (debugIsOn) {
+ for (HandshakeState handshakeState : upcomingStates) {
+ System.out.println(
+ "upcoming handshake states: " + handshakeState);
+ }
+ for (HandshakeState handshakeState : alternatives) {
+ System.out.println(
+ "upcoming handshake alternative state: " + handshakeState);
+ }
+ }
+ }
+
+ void changeCipherSpec(boolean isInput,
+ boolean isClient) throws SSLProtocolException {
+
+ if (debugIsOn) {
+ System.out.println(
+ "update handshake state: change_cipher_spec");
+ }
+
+ String exceptionMsg = "ChangeCipherSpec message sequence violation";
+
+ HandshakeState expectedState;
+ if ((isClient && isInput) || (!isClient && !isInput)) {
+ expectedState = HS_SERVER_CHANGE_CIPHER_SPEC;
+ } else {
+ expectedState = HS_CLIENT_CHANGE_CIPHER_SPEC;
+ }
+
+ boolean hasPresentState = false;
+
+ // Check and update the present state.
+ while (!upcomingStates.isEmpty()) {
+ HandshakeState handshakeState = upcomingStates.pop();
+ if (handshakeState == expectedState) {
+ hasPresentState = true;
+ break;
+ } else if (!handshakeState.isOptional) {
+ throw new SSLProtocolException(exceptionMsg);
+ } // Otherwise, looking for next state track.
+ }
+
+ // No present state.
+ if (!hasPresentState) {
+ throw new SSLProtocolException(exceptionMsg);
+ }
+
+ // no new upcoming states.
+
+ if (debugIsOn) {
+ for (HandshakeState handshakeState : upcomingStates) {
+ System.out.println(
+ "upcoming handshake states: " + handshakeState);
+ }
+ for (HandshakeState handshakeState : alternatives) {
+ System.out.println(
+ "upcoming handshake alternative state: " + handshakeState);
+ }
+ }
+ }
+
+ private static String toString(byte handshakeType) {
+ String s = handshakeTypes.get(handshakeType);
+ if (s == null) {
+ s = "unknown";
+ }
+ return (s + "[" + handshakeType + "]");
+ }
+}
+
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java Thu Jun 04 18:49:37 2015 -0700
@@ -29,6 +29,7 @@
import java.io.*;
import java.util.*;
import java.security.*;
+import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.security.AccessController;
import java.security.AlgorithmConstraints;
@@ -83,7 +84,7 @@
private CipherSuiteList enabledCipherSuites;
// The endpoint identification protocol
- String identificationProtocol;
+ String identificationProtocol;
// The cryptographic algorithm constraints
private AlgorithmConstraints algorithmConstraints = null;
@@ -109,12 +110,15 @@
* Active cipher suites is a subset of enabled cipher suites, and will
* contain only those cipher suites available for the active protocols.
*/
- private CipherSuiteList activeCipherSuites;
+ private CipherSuiteList activeCipherSuites;
// The server name indication and matchers
List<SNIServerName> serverNames = Collections.<SNIServerName>emptyList();
Collection<SNIMatcher> sniMatchers = Collections.<SNIMatcher>emptyList();
+ // The maximum expected network packet size for SSL/TLS/DTLS records.
+ int maximumPacketSize = 0;
+
private boolean isClient;
private boolean needCertVerify;
@@ -124,11 +128,16 @@
HandshakeHash handshakeHash;
HandshakeInStream input;
HandshakeOutStream output;
- int state;
SSLContextImpl sslContext;
RandomCookie clnt_random, svr_random;
SSLSessionImpl session;
+ HandshakeStateManager handshakeState;
+ boolean clientHelloDelivered;
+ boolean serverHelloRequested;
+ boolean handshakeActivated;
+ boolean handshakeFinished;
+
// current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL
CipherSuite cipherSuite;
@@ -141,10 +150,6 @@
// True if it's OK to start a new SSL session
boolean enableNewSession;
- // True if session keys have been calculated and the caller may receive
- // and process a ChangeCipherSpec message
- private boolean sessKeysCalculated;
-
// Whether local cipher suites preference should be honored during
// handshaking?
//
@@ -207,12 +212,18 @@
// need to dispose the object when it is invalidated
boolean invalidated;
+ /*
+ * Is this an instance for Datagram Transport Layer Security (DTLS)?
+ */
+ final boolean isDTLS;
+
Handshaker(SSLSocketImpl c, SSLContextImpl context,
ProtocolList enabledProtocols, boolean needCertVerify,
boolean isClient, ProtocolVersion activeProtocolVersion,
boolean isInitialHandshake, boolean secureRenegotiation,
byte[] clientVerifyData, byte[] serverVerifyData) {
this.conn = c;
+ this.isDTLS = false;
init(context, enabledProtocols, needCertVerify, isClient,
activeProtocolVersion, isInitialHandshake, secureRenegotiation,
clientVerifyData, serverVerifyData);
@@ -222,8 +233,10 @@
ProtocolList enabledProtocols, boolean needCertVerify,
boolean isClient, ProtocolVersion activeProtocolVersion,
boolean isInitialHandshake, boolean secureRenegotiation,
- byte[] clientVerifyData, byte[] serverVerifyData) {
+ byte[] clientVerifyData, byte[] serverVerifyData,
+ boolean isDTLS) {
this.engine = engine;
+ this.isDTLS = isDTLS;
init(context, enabledProtocols, needCertVerify, isClient,
activeProtocolVersion, isInitialHandshake, secureRenegotiation,
clientVerifyData, serverVerifyData);
@@ -251,9 +264,13 @@
this.secureRenegotiation = secureRenegotiation;
this.clientVerifyData = clientVerifyData;
this.serverVerifyData = serverVerifyData;
- enableNewSession = true;
- invalidated = false;
- sessKeysCalculated = false;
+ this.enableNewSession = true;
+ this.invalidated = false;
+ this.handshakeState = new HandshakeStateManager(isDTLS);
+ this.clientHelloDelivered = false;
+ this.serverHelloRequested = false;
+ this.handshakeActivated = false;
+ this.handshakeFinished = false;
setCipherSuite(CipherSuite.C_NULL);
setEnabledProtocols(enabledProtocols);
@@ -263,22 +280,6 @@
} else { // engine != null
algorithmConstraints = new SSLAlgorithmConstraints(engine, true);
}
-
-
- //
- // In addition to the connection state machine, controlling
- // how the connection deals with the different sorts of records
- // that get sent (notably handshake transitions!), there's
- // also a handshaking state machine that controls message
- // sequencing.
- //
- // It's a convenient artifact of the protocol that this can,
- // with only a couple of minor exceptions, be driven by the
- // type constant for the last message seen: except for the
- // client's cert verify, those constants are in a convenient
- // order to drastically simplify state machine checking.
- //
- state = -2; // initialized but not activated
}
/*
@@ -360,14 +361,6 @@
}
}
- final boolean receivedChangeCipherSpec() {
- if (conn != null) {
- return conn.receivedChangeCipherSpec();
- } else {
- return engine.receivedChangeCipherSpec();
- }
- }
-
String getEndpointIdentificationAlgorithmSE() {
SSLParameters paras;
if (conn != null) {
@@ -395,8 +388,6 @@
void setVersion(ProtocolVersion protocolVersion) {
this.protocolVersion = protocolVersion;
setVersionSE(protocolVersion);
-
- output.r.setVersion(protocolVersion);
}
/**
@@ -483,6 +474,13 @@
}
/**
+ * Sets the maximum packet size of the handshaking.
+ */
+ void setMaximumPacketSize(int maximumPacketSize) {
+ this.maximumPacketSize = maximumPacketSize;
+ }
+
+ /**
* Sets the cipher suites preference.
*/
void setUseCipherSuitesOrder(boolean on) {
@@ -532,23 +530,29 @@
handshakeHash = new HandshakeHash(needCertVerify);
// Generate handshake input/output stream.
- input = new HandshakeInStream(handshakeHash);
if (conn != null) {
- output = new HandshakeOutStream(protocolVersion, helloVersion,
- handshakeHash, conn);
- conn.getAppInputStream().r.setHandshakeHash(handshakeHash);
- conn.getAppInputStream().r.setHelloVersion(helloVersion);
- conn.getAppOutputStream().r.setHelloVersion(helloVersion);
- } else {
- output = new HandshakeOutStream(protocolVersion, helloVersion,
- handshakeHash, engine);
+ input = new HandshakeInStream();
+ output = new HandshakeOutStream(conn.outputRecord);
+
+ conn.inputRecord.setHandshakeHash(handshakeHash);
+ conn.inputRecord.setHelloVersion(helloVersion);
+
+ conn.outputRecord.setHandshakeHash(handshakeHash);
+ conn.outputRecord.setHelloVersion(helloVersion);
+ conn.outputRecord.setVersion(protocolVersion);
+ } else if (engine != null) {
+ input = new HandshakeInStream();
+ output = new HandshakeOutStream(engine.outputRecord);
+
engine.inputRecord.setHandshakeHash(handshakeHash);
engine.inputRecord.setHelloVersion(helloVersion);
+
+ engine.outputRecord.setHandshakeHash(handshakeHash);
engine.outputRecord.setHelloVersion(helloVersion);
+ engine.outputRecord.setVersion(protocolVersion);
}
- // move state to activated
- state = -1;
+ handshakeActivated = true;
}
/**
@@ -637,15 +641,15 @@
if (!(activeProtocols.collection().isEmpty()) &&
activeProtocols.min.v != ProtocolVersion.NONE.v) {
for (CipherSuite suite : enabledCipherSuites.collection()) {
- if (suite.obsoleted > activeProtocols.min.v &&
- suite.supported <= activeProtocols.max.v) {
+ if (!activeProtocols.min.obsoletes(suite) &&
+ activeProtocols.max.supports(suite)) {
if (algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
suite.name, null)) {
suites.add(suite);
}
} else if (debug != null && Debug.isOn("verbose")) {
- if (suite.obsoleted <= activeProtocols.min.v) {
+ if (activeProtocols.min.obsoletes(suite)) {
System.out.println(
"Ignoring obsoleted cipher suite: " + suite);
} else {
@@ -700,8 +704,8 @@
boolean found = false;
for (CipherSuite suite : enabledCipherSuites.collection()) {
- if (suite.isAvailable() && suite.obsoleted > protocol.v &&
- suite.supported <= protocol.v) {
+ if (suite.isAvailable() && (!protocol.obsoletes(suite)) &&
+ protocol.supports(suite)) {
if (algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
suite.name, null)) {
@@ -837,7 +841,7 @@
* this freshly created session can become the current one.
*/
boolean isDone() {
- return state == HandshakeMessage.ht_finished;
+ return started() && handshakeState.isEmpty() && handshakeFinished;
}
@@ -861,6 +865,14 @@
}
}
+ void expectingFinishFlightSE() {
+ if (conn != null) {
+ conn.expectingFinishFlight();
+ } else {
+ engine.expectingFinishFlight();
+ }
+ }
+
/*
* Returns true if renegotiation is in use for this connection.
*/
@@ -886,8 +898,8 @@
* This routine is fed SSL handshake records when they become available,
* and processes messages found therein.
*/
- void process_record(InputRecord r, boolean expectingFinished)
- throws IOException {
+ void processRecord(ByteBuffer record,
+ boolean expectingFinished) throws IOException {
checkThrown();
@@ -895,7 +907,7 @@
* Store the incoming handshake data, then see if we can
* now process any completed handshake messages
*/
- input.incomingRecord(r);
+ input.incomingRecord(record);
/*
* We don't need to create a separate delegatable task
@@ -946,6 +958,13 @@
return;
}
+ // Set the flags in the message receiving side.
+ if (messageType == HandshakeMessage.ht_client_hello) {
+ clientHelloDelivered = true;
+ } else if (messageType == HandshakeMessage.ht_hello_request) {
+ serverHelloRequested = true;
+ }
+
/*
* Process the message. We require
* that processMessage() consumes the entire message. In
@@ -961,14 +980,16 @@
* Also, note that hello request messages are never hashed;
* that includes the hello request header, too.
*/
- if (messageType == HandshakeMessage.ht_hello_request) {
- input.reset();
- processMessage(messageType, messageLen);
- input.ignore(4 + messageLen);
- } else {
- input.mark(messageLen);
- processMessage(messageType, messageLen);
- input.digestNow();
+ processMessage(messageType, messageLen);
+
+ // Reload if this message has been reserved.
+ //
+ // Note: in the implementation, only certificate_verify and
+ // finished messages are reserved.
+ if ((messageType == HandshakeMessage.ht_finished) ||
+ (messageType == HandshakeMessage.ht_certificate_verify)) {
+
+ handshakeHash.reload();
}
}
}
@@ -980,29 +1001,29 @@
* In activated state, the handshaker may not send any messages out.
*/
boolean activated() {
- return state >= -1;
+ return handshakeActivated;
}
/**
* Returns true iff the handshaker has sent any messages.
*/
boolean started() {
- return state >= 0; // 0: HandshakeMessage.ht_hello_request
- // 1: HandshakeMessage.ht_client_hello
+ return (serverHelloRequested || clientHelloDelivered);
}
-
/*
* Used to kickstart the negotiation ... either writing a
* ClientHello or a HelloRequest as appropriate, whichever
* the subclass returns. NOP if handshaking's already started.
*/
void kickstart() throws IOException {
- if (state >= 0) {
+ if ((isClient && clientHelloDelivered) ||
+ (!isClient && serverHelloRequested)) {
return;
}
HandshakeMessage m = getKickstartMessage();
+ handshakeState.update(m, resumingSession);
if (debug != null && Debug.isOn("handshake")) {
m.print(System.out);
@@ -1010,7 +1031,13 @@
m.write(output);
output.flush();
- state = m.messageType();
+ // Set the flags in the message delivering side.
+ int handshakeType = m.messageType();
+ if (handshakeType == HandshakeMessage.ht_hello_request) {
+ serverHelloRequested = true;
+ } else { // HandshakeMessage.ht_client_hello
+ clientHelloDelivered = true;
+ }
}
/**
@@ -1052,24 +1079,16 @@
* We already hold SSLEngine/SSLSocket "this" by virtue
* of this being called from the readRecord code.
*/
- OutputRecord r;
- if (conn != null) {
- r = new OutputRecord(Record.ct_change_cipher_spec);
- } else {
- r = new EngineOutputRecord(Record.ct_change_cipher_spec, engine);
- }
-
- r.setVersion(protocolVersion);
- r.write(1); // single byte of data
-
if (conn != null) {
conn.writeLock.lock();
try {
- conn.writeRecord(r);
+ handshakeState.changeCipherSpec(false, isClient);
conn.changeWriteCiphers();
if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out);
}
+
+ handshakeState.update(mesg, resumingSession);
mesg.write(output);
output.flush();
} finally {
@@ -1077,19 +1096,25 @@
}
} else {
synchronized (engine.writeLock) {
- engine.writeRecord((EngineOutputRecord)r);
+ handshakeState.changeCipherSpec(false, isClient);
engine.changeWriteCiphers();
if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out);
}
+
+ handshakeState.update(mesg, resumingSession);
mesg.write(output);
-
- if (lastMessage) {
- output.setFinishedMsg();
- }
output.flush();
}
}
+
+ if (lastMessage) {
+ handshakeFinished = true;
+ }
+ }
+
+ void receiveChangeCipherSpec() throws IOException {
+ handshakeState.changeCipherSpec(true, isClient);
}
/*
@@ -1131,12 +1156,31 @@
String masterAlg;
PRF prf;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
- masterAlg = "SunTls12MasterSecret";
- prf = cipherSuite.prfAlg;
+ byte majorVersion = protocolVersion.major;
+ byte minorVersion = protocolVersion.minor;
+ if (protocolVersion.isDTLSProtocol()) {
+ // Use TLS version number for DTLS key calculation
+ if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
+ majorVersion = ProtocolVersion.TLS11.major;
+ minorVersion = ProtocolVersion.TLS11.minor;
+
+ masterAlg = "SunTlsMasterSecret";
+ prf = P_NONE;
+ } else { // DTLS 1.2
+ majorVersion = ProtocolVersion.TLS12.major;
+ minorVersion = ProtocolVersion.TLS12.minor;
+
+ masterAlg = "SunTls12MasterSecret";
+ prf = cipherSuite.prfAlg;
+ }
} else {
- masterAlg = "SunTlsMasterSecret";
- prf = P_NONE;
+ if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ masterAlg = "SunTls12MasterSecret";
+ prf = cipherSuite.prfAlg;
+ } else {
+ masterAlg = "SunTlsMasterSecret";
+ prf = P_NONE;
+ }
}
String prfHashAlg = prf.getPRFHashAlg();
@@ -1145,7 +1189,7 @@
@SuppressWarnings("deprecation")
TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec(
- preMasterSecret, protocolVersion.major, protocolVersion.minor,
+ preMasterSecret, (majorVersion & 0xFF), (minorVersion & 0xFF),
clnt_random.random_bytes, svr_random.random_bytes,
prfHashAlg, prfHashLength, prfBlockSize);
@@ -1196,36 +1240,55 @@
String keyMaterialAlg;
PRF prf;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
- keyMaterialAlg = "SunTls12KeyMaterial";
- prf = cipherSuite.prfAlg;
+ byte majorVersion = protocolVersion.major;
+ byte minorVersion = protocolVersion.minor;
+ if (protocolVersion.isDTLSProtocol()) {
+ // Use TLS version number for DTLS key calculation
+ if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
+ majorVersion = ProtocolVersion.TLS11.major;
+ minorVersion = ProtocolVersion.TLS11.minor;
+
+ keyMaterialAlg = "SunTlsKeyMaterial";
+ prf = P_NONE;
+ } else { // DTLS 1.2+
+ majorVersion = ProtocolVersion.TLS12.major;
+ minorVersion = ProtocolVersion.TLS12.minor;
+
+ keyMaterialAlg = "SunTls12KeyMaterial";
+ prf = cipherSuite.prfAlg;
+ }
} else {
- keyMaterialAlg = "SunTlsKeyMaterial";
- prf = P_NONE;
+ if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ keyMaterialAlg = "SunTls12KeyMaterial";
+ prf = cipherSuite.prfAlg;
+ } else {
+ keyMaterialAlg = "SunTlsKeyMaterial";
+ prf = P_NONE;
+ }
}
String prfHashAlg = prf.getPRFHashAlg();
int prfHashLength = prf.getPRFHashLength();
int prfBlockSize = prf.getPRFBlockSize();
- // TLS v1.1 or later uses an explicit IV in CBC cipher suites to
+ // TLS v1.1+ and DTLS use an explicit IV in CBC cipher suites to
// protect against the CBC attacks. AEAD/GCM cipher suites in TLS
// v1.2 or later use a fixed IV as the implicit part of the partially
// implicit nonce technique described in RFC 5116.
int ivSize = cipher.ivSize;
if (cipher.cipherType == AEAD_CIPHER) {
ivSize = cipher.fixedIvSize;
- } else if (protocolVersion.v >= ProtocolVersion.TLS11.v &&
- cipher.cipherType == BLOCK_CIPHER) {
+ } else if ((cipher.cipherType == BLOCK_CIPHER) &&
+ protocolVersion.useTLS11PlusSpec()) {
ivSize = 0;
}
TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec(
- masterKey, protocolVersion.major, protocolVersion.minor,
- clnt_random.random_bytes, svr_random.random_bytes,
- cipher.algorithm, cipher.keySize, expandedKeySize,
- ivSize, hashSize,
- prfHashAlg, prfHashLength, prfBlockSize);
+ masterKey, (majorVersion & 0xFF), (minorVersion & 0xFF),
+ clnt_random.random_bytes, svr_random.random_bytes,
+ cipher.algorithm, cipher.keySize, expandedKeySize,
+ ivSize, hashSize,
+ prfHashAlg, prfHashLength, prfBlockSize);
try {
KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg);
@@ -1247,10 +1310,6 @@
throw new ProviderException(e);
}
- // Mark a flag that allows outside entities (like SSLSocket/SSLEngine)
- // determine if a ChangeCipherSpec message could be processed.
- sessKeysCalculated = true;
-
//
// Dump the connection keys as they're generated.
//
@@ -1293,7 +1352,7 @@
System.out.println("Server write IV:");
printHex(dump, svrWriteIV.getIV());
} else {
- if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ if (protocolVersion.useTLS11PlusSpec()) {
System.out.println(
"... no IV derived for this protocol");
} else {
@@ -1305,15 +1364,6 @@
}
}
- /**
- * Return whether or not the Handshaker has derived session keys for
- * this handshake. This is used for determining readiness to process
- * an incoming ChangeCipherSpec message.
- */
- boolean sessionKeysCalculated() {
- return sessKeysCalculated;
- }
-
private static void printHex(HexDumpEncoder dump, byte[] bytes) {
if (bytes == null) {
System.out.println("(key bytes not available)");
@@ -1326,19 +1376,6 @@
}
}
- /**
- * Throw an SSLException with the specified message and cause.
- * Shorthand until a new SSLException constructor is added.
- * This method never returns.
- */
- static void throwSSLException(String msg, Throwable cause)
- throws SSLException {
- SSLException e = new SSLException(msg);
- e.initCause(cause);
- throw e;
- }
-
-
/*
* Implement a simple task delegator.
*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+import javax.net.ssl.SSLProtocolException;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+
+import sun.security.ssl.HandshakeMessage.ClientHello;
+
+/*
+ * HelloVerifyRequest cookie manager
+ */
+final class HelloCookieManager {
+ // the cookie secret life time
+ private static long COOKIE_TIMING_WINDOW = 3600000; // in milliseconds
+ private static int COOKIE_MAX_LENGTH_DTLS10 = 32; // 32 bytes
+ private static int COOKIE_MAX_LENGTH_DTLS12 = 0xFF; // 2^8 -1 bytes
+
+ private final SecureRandom secureRandom;
+ private final MessageDigest cookieDigest;
+
+ private int cookieVersion; // allow to wrap
+ private long secretLifetime;
+ private byte[] cookieSecret;
+
+ private int prevCookieVersion;
+ private byte[] prevCookieSecret;
+
+ HelloCookieManager(SecureRandom secureRandom) {
+ this.secureRandom = secureRandom;
+ this.cookieDigest = JsseJce.getMessageDigest("SHA-256");
+
+ this.cookieVersion = secureRandom.nextInt();
+ this.secretLifetime = 0;
+ this.cookieSecret = null;
+
+ this.prevCookieVersion = 0;
+ this.prevCookieSecret = null;
+ }
+
+ // Used by server side to generate cookies in HelloVerifyRequest message.
+ synchronized byte[] getCookie(ClientHello clientHelloMsg) {
+ if (secretLifetime < System.currentTimeMillis()) {
+ if (cookieSecret != null) {
+ prevCookieVersion = cookieVersion;
+ prevCookieSecret = cookieSecret.clone();
+ } else {
+ cookieSecret = new byte[32];
+ }
+
+ cookieVersion++;
+ secureRandom.nextBytes(cookieSecret);
+ secretLifetime = System.currentTimeMillis() + COOKIE_TIMING_WINDOW;
+ }
+
+ clientHelloMsg.updateHelloCookie(cookieDigest);
+ byte[] cookie = cookieDigest.digest(cookieSecret); // 32 bytes
+ cookie[0] = (byte)((cookieVersion >> 24) & 0xFF);
+ cookie[1] = (byte)((cookieVersion >> 16) & 0xFF);
+ cookie[2] = (byte)((cookieVersion >> 8) & 0xFF);
+ cookie[3] = (byte)(cookieVersion & 0xFF);
+
+ return cookie;
+ }
+
+ // Used by server side to check the cookie in ClientHello message.
+ synchronized boolean isValid(ClientHello clientHelloMsg) {
+ byte[] cookie = clientHelloMsg.cookie;
+
+ // no cookie exchange or not a valid cookie length
+ if ((cookie == null) || (cookie.length != 32)) {
+ return false;
+ }
+
+ int version = ((cookie[0] & 0xFF) << 24) |
+ ((cookie[1] & 0xFF) << 16) |
+ ((cookie[2] & 0xFF) << 8) |
+ (cookie[3] & 0xFF);
+
+ byte[] secret;
+ if (version == cookieVersion) {
+ secret = cookieSecret;
+ } else if (version == prevCookieVersion) {
+ secret = prevCookieSecret;
+ } else {
+ return false; // may be out of the timing window
+ }
+
+ clientHelloMsg.updateHelloCookie(cookieDigest);
+ byte[] target = cookieDigest.digest(secret); // 32 bytes
+
+ for (int i = 4; i < 32; i++) {
+ if (cookie[i] != target[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Used by client side to check the cookie in HelloVerifyRequest message.
+ static void checkCookie(ProtocolVersion protocolVersion,
+ byte[] cookie) throws IOException {
+ if (cookie != null && cookie.length != 0) {
+ int limit = COOKIE_MAX_LENGTH_DTLS12;
+ if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
+ limit = COOKIE_MAX_LENGTH_DTLS10;
+ }
+
+ if (cookie.length > COOKIE_MAX_LENGTH_DTLS10) {
+ throw new SSLProtocolException(
+ "Invalid HelloVerifyRequest.cookie (length = " +
+ cookie.length + " bytes)");
+ }
+ }
+
+ // Otherwise, no cookie exchange.
+ }
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java Thu Jun 04 18:49:37 2015 -0700
@@ -85,6 +85,8 @@
new SupportedEllipticPointFormatsExtension(s, extlen);
} else if (extType == ExtensionType.EXT_RENEGOTIATION_INFO) {
extension = new RenegotiationInfoExtension(s, extlen);
+ } else if (extType == ExtensionType.EXT_MAX_FRAGMENT_LENGTH) {
+ extension = new MaxFragmentLengthExtension(s, extlen);
} else {
extension = new UnknownExtension(s, extlen, extType);
}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/InputRecord.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/InputRecord.java Thu Jun 04 18:49:37 2015 -0700
@@ -23,11 +23,11 @@
* questions.
*/
-
package sun.security.ssl;
import java.io.*;
import java.nio.*;
+import java.util.*;
import javax.crypto.BadPaddingException;
@@ -37,66 +37,35 @@
/**
- * SSL 3.0 records, as pulled off a TCP stream. Input records are
- * basically buffers tied to a particular input stream ... a layer
- * above this must map these records into the model of a continuous
- * stream of data.
- *
- * Since this returns SSL 3.0 records, it's the layer that needs to
- * map SSL 2.0 style handshake records into SSL 3.0 ones for those
- * "old" clients that interop with both V2 and V3 servers. Not as
- * pretty as might be desired.
- *
- * NOTE: During handshaking, each message must be hashed to support
- * verification that the handshake process wasn't compromised.
+ * {@code InputRecord} takes care of the management of SSL/TLS/DTLS input
+ * records, including buffering, decryption, handshake messages marshal, etc.
*
* @author David Brownell
*/
-class InputRecord extends ByteArrayInputStream implements Record {
+class InputRecord implements Record, Closeable {
+
+ /* Class and subclass dynamic debugging support */
+ static final Debug debug = Debug.getInstance("ssl");
- private HandshakeHash handshakeHash;
- private int lastHashed;
- boolean formatVerified = true; // SSLv2 ruled out?
- private boolean isClosed;
- private boolean appDataValid;
+ Authenticator readAuthenticator;
+ CipherBox readCipher;
+
+ HandshakeHash handshakeHash;
+ boolean isClosed;
// The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello
// and the first message we read is a ClientHello in V2 format, we convert
// it to V3. Otherwise we throw an exception when encountering a V2 hello.
- private ProtocolVersion helloVersion;
-
- /* Class and subclass dynamic debugging support */
- static final Debug debug = Debug.getInstance("ssl");
+ ProtocolVersion helloVersion;
- /* The existing record length */
- private int exlen;
-
- /* V2 handshake message */
- private byte v2Buf[];
+ // fragment size
+ int fragmentSize;
- /*
- * Construct the record to hold the maximum sized input record.
- * Data will be filled in separately.
- *
- * The structure of the byte buffer looks like:
- *
- * |--------+---------+---------------------------------|
- * | header | IV | content, MAC/TAG, padding, etc. |
- * | headerPlusIVSize |
- *
- * header: the header of an SSL records
- * IV: the optional IV/nonce field, it is only required for block
- * (TLS 1.1 or later) and AEAD cipher suites.
- *
- */
InputRecord() {
- super(new byte[maxRecordSize]);
- setHelloVersion(ProtocolVersion.DEFAULT_HELLO);
- pos = headerSize;
- count = headerSize;
- lastHashed = count;
- exlen = 0;
- v2Buf = null;
+ this.readCipher = CipherBox.NULL;
+ this.readAuthenticator = null; // Please override this assignment.
+ this.helloVersion = ProtocolVersion.DEFAULT_HELLO;
+ this.fragmentSize = Record.maxDataSize;
}
void setHelloVersion(ProtocolVersion helloVersion) {
@@ -108,70 +77,332 @@
}
/*
- * Enable format checks if initial handshaking hasn't completed
- */
- void enableFormatChecks() {
- formatVerified = false;
- }
-
- // return whether the data in this record is valid, decrypted data
- boolean isAppDataValid() {
- return appDataValid;
- }
-
- void setAppDataValid(boolean value) {
- appDataValid = value;
- }
-
- /*
- * Return the content type of the record.
- */
- byte contentType() {
- return buf[0];
- }
-
- /*
+ * Set instance for the computation of handshake hashes.
+ *
* For handshaking, we need to be able to hash every byte above the
* record marking layer. This is where we're guaranteed to see those
* bytes, so this is where we can hash them ... especially in the
* case of hashing the initial V2 message!
*/
void setHandshakeHash(HandshakeHash handshakeHash) {
+ if (handshakeHash != null) {
+ byte[] reserved = null;
+ if (this.handshakeHash != null) {
+ reserved = this.handshakeHash.getAllHandshakeMessages();
+ }
+ if ((reserved != null) && (reserved.length != 0)) {
+ handshakeHash.update(reserved, 0, reserved.length);
+
+ if (debug != null && Debug.isOn("data")) {
+ Debug.printHex(
+ "[reserved] handshake hash: len = " + reserved.length,
+ reserved);
+ }
+ }
+ }
+
this.handshakeHash = handshakeHash;
}
- HandshakeHash getHandshakeHash() {
- return handshakeHash;
+ boolean seqNumIsHuge() {
+ return (readAuthenticator != null) &&
+ readAuthenticator.seqNumIsHuge();
+ }
+
+ boolean isEmpty() {
+ return false;
+ }
+
+ // apply to DTLS SSLEngine
+ void expectingFinishFlight() {
+ // blank
+ }
+
+ /**
+ * Prevent any more data from being read into this record,
+ * and flag the record as holding no data.
+ */
+ @Override
+ synchronized public void close() throws IOException {
+ if (!isClosed) {
+ isClosed = true;
+ readCipher.dispose();
+ }
+ }
+
+ // apply to SSLSocket and SSLEngine
+ void changeReadCiphers(
+ Authenticator readAuthenticator, CipherBox readCipher) {
+
+ /*
+ * Dispose of any intermediate state in the underlying cipher.
+ * For PKCS11 ciphers, this will release any attached sessions,
+ * and thus make finalization faster.
+ *
+ * Since MAC's doFinal() is called for every SSL/TLS packet, it's
+ * not necessary to do the same with MAC's.
+ */
+ readCipher.dispose();
+
+ this.readAuthenticator = readAuthenticator;
+ this.readCipher = readCipher;
+ }
+
+ // change fragment size
+ void changeFragmentSize(int fragmentSize) {
+ this.fragmentSize = fragmentSize;
+ }
+
+ /*
+ * Check if there is enough inbound data in the ByteBuffer to make
+ * a inbound packet.
+ *
+ * @return -1 if there are not enough bytes to tell (small header),
+ */
+ // apply to SSLEngine only
+ int bytesInCompletePacket(ByteBuffer buf) throws SSLException {
+ throw new UnsupportedOperationException();
+ }
+
+ // apply to SSLSocket only
+ int bytesInCompletePacket(InputStream is) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Return true if the specified record protocol version is out of the
+ * range of the possible supported versions.
+ */
+ void checkRecordVersion(ProtocolVersion version,
+ boolean allowSSL20Hello) throws SSLException {
+ // blank
+ }
+
+ // apply to DTLS SSLEngine only
+ Plaintext acquirePlaintext()
+ throws IOException, BadPaddingException {
+ throw new UnsupportedOperationException();
+ }
+
+ // read, decrypt and decompress the network record.
+ //
+ // apply to SSLEngine only
+ Plaintext decode(ByteBuffer netData)
+ throws IOException, BadPaddingException {
+ throw new UnsupportedOperationException();
+ }
+
+ // apply to SSLSocket only
+ Plaintext decode(InputStream is, ByteBuffer destination)
+ throws IOException, BadPaddingException {
+ throw new UnsupportedOperationException();
+ }
+
+ // apply to SSLSocket only
+ void setDeliverStream(OutputStream outputStream) {
+ throw new UnsupportedOperationException();
+ }
+
+ // calculate plaintext fragment size
+ //
+ // apply to SSLEngine only
+ int estimateFragmentSize(int packetSize) {
+ throw new UnsupportedOperationException();
}
- void decrypt(Authenticator authenticator,
- CipherBox box) throws BadPaddingException {
+ //
+ // shared helpers
+ //
+
+ // Not apply to DTLS
+ static ByteBuffer convertToClientHello(ByteBuffer packet) {
+
+ int srcPos = packet.position();
+ int srcLim = packet.limit();
+
+ byte firstByte = packet.get();
+ byte secondByte = packet.get();
+ int recordLen = (((firstByte & 0x7F) << 8) | (secondByte & 0xFF)) + 2;
+
+ packet.position(srcPos + 3); // the V2ClientHello record header
+
+ byte majorVersion = packet.get();
+ byte minorVersion = packet.get();
+
+ int cipherSpecLen = ((packet.get() & 0xFF) << 8) +
+ (packet.get() & 0xFF);
+ int sessionIdLen = ((packet.get() & 0xFF) << 8) +
+ (packet.get() & 0xFF);
+ int nonceLen = ((packet.get() & 0xFF) << 8) +
+ (packet.get() & 0xFF);
+
+ // Required space for the target SSLv3 ClientHello message.
+ // 5: record header size
+ // 4: handshake header size
+ // 2: ClientHello.client_version
+ // 32: ClientHello.random
+ // 1: length byte of ClientHello.session_id
+ // 2: empty ClientHello.compression_methods
+ int requiredSize = 46 + sessionIdLen + ((cipherSpecLen * 2 ) / 3 );
+ byte[] converted = new byte[requiredSize];
+
+ /*
+ * Build the first part of the V3 record header from the V2 one
+ * that's now buffered up. (Lengths are fixed up later).
+ */
+ // Note: need not to set the header actually.
+ converted[0] = ct_handshake;
+ converted[1] = majorVersion;
+ converted[2] = minorVersion;
+ // header [3..4] for handshake message length
+ // required size is 5;
+
+ /*
+ * Store the generic V3 handshake header: 4 bytes
+ */
+ converted[5] = 1; // HandshakeMessage.ht_client_hello
+ // buf [6..8] for length of ClientHello (int24)
+ // required size += 4;
+
+ /*
+ * ClientHello header starts with SSL version
+ */
+ converted[9] = majorVersion;
+ converted[10] = minorVersion;
+ // required size += 2;
+ int pointer = 11;
+
+ /*
+ * Copy Random value/nonce ... if less than the 32 bytes of
+ * a V3 "Random", right justify and zero pad to the left. Else
+ * just take the last 32 bytes.
+ */
+ int offset = srcPos + 11 + cipherSpecLen + sessionIdLen;
+
+ if (nonceLen < 32) {
+ for (int i = 0; i < (32 - nonceLen); i++) {
+ converted[pointer++] = 0;
+ }
+ packet.position(offset);
+ packet.get(converted, pointer, nonceLen);
+
+ pointer += nonceLen;
+ } else {
+ packet.position(offset + nonceLen - 32);
+ packet.get(converted, pointer, 32);
+
+ pointer += 32;
+ }
+
+ /*
+ * Copy session ID (only one byte length!)
+ */
+ offset -= sessionIdLen;
+ converted[pointer++] = (byte)(sessionIdLen & 0xFF);
+ packet.position(offset);
+ packet.get(converted, pointer, sessionIdLen);
+
+ /*
+ * Copy and translate cipher suites ... V2 specs with first byte zero
+ * are really V3 specs (in the last 2 bytes), just copy those and drop
+ * the other ones. Preference order remains unchanged.
+ *
+ * Example: Netscape Navigator 3.0 (exportable) says:
+ *
+ * 0/3, SSL_RSA_EXPORT_WITH_RC4_40_MD5
+ * 0/6, SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5
+ *
+ * Microsoft Internet Explorer 3.0 (exportable) supports only
+ *
+ * 0/3, SSL_RSA_EXPORT_WITH_RC4_40_MD5
+ */
+ int j;
+
+ offset -= cipherSpecLen;
+ packet.position(offset);
+
+ j = pointer + 2;
+ for (int i = 0; i < cipherSpecLen; i += 3) {
+ if (packet.get() != 0) {
+ // Ignore version 2.0 specifix cipher suite. Clients
+ // should also include the version 3.0 equivalent in
+ // the V2ClientHello message.
+ packet.get(); // ignore the 2nd byte
+ packet.get(); // ignore the 3rd byte
+ continue;
+ }
+
+ converted[j++] = packet.get();
+ converted[j++] = packet.get();
+ }
+
+ j -= pointer + 2;
+ converted[pointer++] = (byte)((j >>> 8) & 0xFF);
+ converted[pointer++] = (byte)(j & 0xFF);
+ pointer += j;
+
+ /*
+ * Append compression methods (default/null only)
+ */
+ converted[pointer++] = 1;
+ converted[pointer++] = 0; // Session.compression_null
+
+ /*
+ * Fill in lengths of the messages we synthesized (nested:
+ * V3 handshake message within V3 record).
+ */
+ // Note: need not to set the header actually.
+ int fragLen = pointer - 5; // TLSPlaintext.length
+ converted[3] = (byte)((fragLen >>> 8) & 0xFF);
+ converted[4] = (byte)(fragLen & 0xFF);
+
+ /*
+ * Handshake.length, length of ClientHello message
+ */
+ fragLen = pointer - 9; // Handshake.length
+ converted[6] = (byte)((fragLen >>> 16) & 0xFF);
+ converted[7] = (byte)((fragLen >>> 8) & 0xFF);
+ converted[8] = (byte)(fragLen & 0xFF);
+
+ // consume the full record
+ packet.position(srcPos + recordLen);
+
+ // Need no header bytes.
+ return ByteBuffer.wrap(converted, 5, pointer - 5); // 5: header size
+ }
+
+ static ByteBuffer decrypt(Authenticator authenticator, CipherBox box,
+ byte contentType, ByteBuffer bb) throws BadPaddingException {
+
+ return decrypt(authenticator, box, contentType, bb, null);
+ }
+
+ static ByteBuffer decrypt(Authenticator authenticator,
+ CipherBox box, byte contentType, ByteBuffer bb,
+ byte[] sequence) throws BadPaddingException {
+
BadPaddingException reservedBPE = null;
int tagLen =
(authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0;
- int cipheredLength = count - headerSize;
-
+ int cipheredLength = bb.remaining();
+ int srcPos = bb.position();
if (!box.isNullCipher()) {
try {
// apply explicit nonce for AEAD/CBC cipher suites if needed
- int nonceSize = box.applyExplicitNonce(authenticator,
- contentType(), buf, headerSize, cipheredLength);
- pos = headerSize + nonceSize;
- lastHashed = pos; // don't digest the explicit nonce
+ int nonceSize = box.applyExplicitNonce(
+ authenticator, contentType, bb, sequence);
// decrypt the content
- int offset = headerSize;
if (box.isAEADMode()) {
- // DON'T encrypt the nonce_explicit for AEAD mode
- offset += nonceSize;
+ // DON'T decrypt the nonce_explicit for AEAD mode
+ bb.position(srcPos + nonceSize);
} // The explicit IV for CBC mode can be decrypted.
// Note that the CipherBox.decrypt() does not change
// the capacity of the buffer.
- count = offset +
- box.decrypt(buf, offset, count - offset, tagLen);
-
- // Note that we don't remove the nonce from the buffer.
+ box.decrypt(bb, tagLen);
+ // We don't actually remove the nonce.
+ bb.position(srcPos + nonceSize);
} catch (BadPaddingException bpe) {
// RFC 2246 states that decryption_failed should be used
// for this purpose. However, that allows certain attacks,
@@ -187,10 +418,9 @@
// Requires message authentication code for null, stream and block
// cipher suites.
- if (authenticator instanceof MAC && tagLen != 0) {
+ if ((authenticator instanceof MAC) && (tagLen != 0)) {
MAC signer = (MAC)authenticator;
- int macOffset = count - tagLen;
- int contentLen = macOffset - pos;
+ int contentLen = bb.remaining() - tagLen;
// Note that although it is not necessary, we run the same MAC
// computation and comparison on the payload for both stream
@@ -202,19 +432,14 @@
}
// set offset of the dummy MAC
- macOffset = headerSize + cipheredLength - tagLen;
- contentLen = macOffset - headerSize;
+ contentLen = cipheredLength - tagLen;
+ bb.limit(srcPos + cipheredLength);
}
- count -= tagLen; // Set the count before any MAC checking
- // exception occurs, so that the following
- // process can read the actual decrypted
- // content (minus the MAC) in the fragment
- // if necessary.
-
// Run MAC computation and comparison on the payload.
- if (checkMacTags(contentType(),
- buf, pos, contentLen, signer, false)) {
+ //
+ // MAC data would be stripped off during the check.
+ if (checkMacTags(contentType, bb, signer, sequence, false)) {
if (reservedBPE == null) {
reservedBPE = new BadPaddingException("bad record MAC");
}
@@ -229,21 +454,18 @@
signer, cipheredLength, contentLen);
// NOTE: remainingLen may be bigger (less than 1 block of the
- // hash algorithm of the MAC) than the cipheredLength. However,
- // We won't need to worry about it because we always use a
- // maximum buffer for every record. We need a change here if
- // we use small buffer size in the future.
- if (remainingLen > buf.length) {
- // unlikely to happen, just a placehold
- throw new RuntimeException(
- "Internal buffer capacity error");
- }
+ // hash algorithm of the MAC) than the cipheredLength.
+ //
+ // Is it possible to use a static buffer, rather than allocate
+ // it dynamically?
+ remainingLen += signer.MAClen();
+ ByteBuffer temporary = ByteBuffer.allocate(remainingLen);
// Won't need to worry about the result on the remainder. And
// then we won't need to worry about what's actual data to
// check MAC tag on. We start the check from the header of the
// buffer so that we don't need to construct a new byte buffer.
- checkMacTags(contentType(), buf, 0, remainingLen, signer, true);
+ checkMacTags(contentType, temporary, signer, sequence, true);
}
}
@@ -251,6 +473,63 @@
if (reservedBPE != null) {
throw reservedBPE;
}
+
+ return bb.slice();
+ }
+
+ /*
+ * Run MAC computation and comparison
+ *
+ */
+ private static boolean checkMacTags(byte contentType, ByteBuffer bb,
+ MAC signer, byte[] sequence, boolean isSimulated) {
+
+ int tagLen = signer.MAClen();
+ int position = bb.position();
+ int lim = bb.limit();
+ int macOffset = lim - tagLen;
+
+ bb.limit(macOffset);
+ byte[] hash = signer.compute(contentType, bb, sequence, isSimulated);
+ if (hash == null || tagLen != hash.length) {
+ // Something is wrong with MAC implementation.
+ throw new RuntimeException("Internal MAC error");
+ }
+
+ bb.position(macOffset);
+ bb.limit(lim);
+ try {
+ int[] results = compareMacTags(bb, hash);
+ return (results[0] != 0);
+ } finally {
+ // reset to the data
+ bb.position(position);
+ bb.limit(macOffset);
+ }
+ }
+
+ /*
+ * A constant-time comparison of the MAC tags.
+ *
+ * Please DON'T change the content of the ByteBuffer parameter!
+ */
+ private static int[] compareMacTags(ByteBuffer bb, byte[] tag) {
+
+ // An array of hits is used to prevent Hotspot optimization for
+ // the purpose of a constant-time check.
+ int[] results = {0, 0}; // {missed #, matched #}
+
+ // The caller ensures there are enough bytes available in the buffer.
+ // So we won't need to check the remaining of the buffer.
+ for (int i = 0; i < tag.length; i++) {
+ if (bb.get() != tag[i]) {
+ results[0]++; // mismatched bytes
+ } else {
+ results[1]++; // matched bytes
+ }
+ }
+
+ return results;
}
/*
@@ -258,7 +537,7 @@
*
* Please DON'T change the content of the byte buffer parameter!
*/
- static boolean checkMacTags(byte contentType, byte[] buffer,
+ private static boolean checkMacTags(byte contentType, byte[] buffer,
int offset, int contentLen, MAC signer, boolean isSimulated) {
int tagLen = signer.MAClen();
@@ -304,7 +583,7 @@
*
* The caller MUST ensure that the fullLen is not less than usedLen.
*/
- static int calculateRemainingLen(
+ private static int calculateRemainingLen(
MAC signer, int fullLen, int usedLen) {
int blockLen = signer.hashBlockLen();
@@ -322,551 +601,7 @@
// that the return value is positive. The extra one byte does
// not impact the overall MAC compression function evaluations.
return 0x01 + (int)(Math.ceil(fullLen/(1.0d * blockLen)) -
- Math.ceil(usedLen/(1.0d * blockLen))) * signer.hashBlockLen();
- }
-
- /*
- * Well ... hello_request messages are _never_ hashed since we can't
- * know when they'd appear in the sequence.
- */
- void ignore(int bytes) {
- if (bytes > 0) {
- pos += bytes;
- lastHashed = pos;
- }
- }
-
- /*
- * We hash the (plaintext) we've processed, but only on demand.
- *
- * There is one place where we want to access the hash in the middle
- * of a record: client cert message gets hashed, and part of the
- * same record is the client cert verify message which uses that hash.
- * So we track how much we've read and hashed.
- */
- void doHashes() {
- int len = pos - lastHashed;
-
- if (len > 0) {
- hashInternal(buf, lastHashed, len);
- lastHashed = pos;
- }
- }
-
- /*
- * Need a helper function so we can hash the V2 hello correctly
- */
- private void hashInternal(byte databuf [], int offset, int len) {
- if (debug != null && Debug.isOn("data")) {
- try {
- HexDumpEncoder hd = new HexDumpEncoder();
-
- System.out.println("[read] MD5 and SHA1 hashes: len = "
- + len);
- hd.encodeBuffer(new ByteArrayInputStream(databuf, offset, len),
- System.out);
- } catch (IOException e) { }
- }
- handshakeHash.update(databuf, offset, len);
- }
-
-
- /*
- * Handshake messages may cross record boundaries. We "queue"
- * these in big buffers if we need to cope with this problem.
- * This is not anticipated to be a common case; if this turns
- * out to be wrong, this can readily be sped up.
- */
- void queueHandshake(InputRecord r) throws IOException {
- int len;
-
- /*
- * Hash any data that's read but unhashed.
- */
- doHashes();
-
- /*
- * Move any unread data to the front of the buffer,
- * flagging it all as unhashed.
- */
- if (pos > headerSize) {
- len = count - pos;
- if (len != 0) {
- System.arraycopy(buf, pos, buf, headerSize, len);
- }
- pos = headerSize;
- lastHashed = pos;
- count = headerSize + len;
- }
-
- /*
- * Grow "buf" if needed
- */
- len = r.available() + count;
- if (buf.length < len) {
- byte newbuf [];
-
- newbuf = new byte [len];
- System.arraycopy(buf, 0, newbuf, 0, count);
- buf = newbuf;
- }
-
- /*
- * Append the new buffer to this one.
- */
- System.arraycopy(r.buf, r.pos, buf, count, len - count);
- count = len;
-
- /*
- * Adjust lastHashed; important for now with clients which
- * send SSL V2 client hellos. This will go away eventually,
- * by buffer code cleanup.
- */
- len = r.lastHashed - r.pos;
- if (pos == headerSize) {
- lastHashed += len;
- } else {
- throw new SSLProtocolException("?? confused buffer hashing ??");
- }
- // we've read the record, advance the pointers
- r.pos = r.count;
- }
-
-
- /**
- * Prevent any more data from being read into this record,
- * and flag the record as holding no data.
- */
- @Override
- public void close() {
- appDataValid = false;
- isClosed = true;
- mark = 0;
- pos = 0;
- count = 0;
+ Math.ceil(usedLen/(1.0d * blockLen))) * blockLen;
}
-
-
- /*
- * We may need to send this SSL v2 "No Cipher" message back, if we
- * are faced with an SSLv2 "hello" that's not saying "I talk v3".
- * It's the only one documented in the V2 spec as a fatal error.
- */
- private static final byte[] v2NoCipher = {
- (byte)0x80, (byte)0x03, // unpadded 3 byte record
- (byte)0x00, // ... error message
- (byte)0x00, (byte)0x01 // ... NO_CIPHER error
- };
-
- private int readFully(InputStream s, byte b[], int off, int len)
- throws IOException {
- int n = 0;
- while (n < len) {
- int readLen = s.read(b, off + n, len - n);
- if (readLen < 0) {
- return readLen;
- }
-
- if (debug != null && Debug.isOn("packet")) {
- try {
- HexDumpEncoder hd = new HexDumpEncoder();
- ByteBuffer bb = ByteBuffer.wrap(b, off + n, readLen);
-
- System.out.println("[Raw read]: length = " +
- bb.remaining());
- hd.encodeBuffer(bb, System.out);
- } catch (IOException e) { }
- }
-
- n += readLen;
- exlen += readLen;
- }
-
- return n;
- }
-
- /*
- * Read the SSL V3 record ... first time around, check to see if it
- * really IS a V3 record. Handle SSL V2 clients which can talk V3.0,
- * as well as real V3 record format; otherwise report an error.
- */
- void read(InputStream s, OutputStream o) throws IOException {
- if (isClosed) {
- return;
- }
-
- /*
- * For SSL it really _is_ an error if the other end went away
- * so ungracefully as to not shut down cleanly.
- */
- if(exlen < headerSize) {
- int really = readFully(s, buf, exlen, headerSize - exlen);
- if (really < 0) {
- throw new EOFException("SSL peer shut down incorrectly");
- }
-
- pos = headerSize;
- count = headerSize;
- lastHashed = pos;
- }
-
- /*
- * The first record might use some other record marking convention,
- * typically SSL v2 header. (PCT could also be detected here.)
- * This case is currently common -- Navigator 3.0 usually works
- * this way, as do IE 3.0 and other products.
- */
- if (!formatVerified) {
- formatVerified = true;
- /*
- * The first record must either be a handshake record or an
- * alert message. If it's not, it is either invalid or an
- * SSLv2 message.
- */
- if (buf[0] != ct_handshake && buf[0] != ct_alert) {
- handleUnknownRecord(s, o);
- } else {
- readV3Record(s, o);
- }
- } else { // formatVerified == true
- readV3Record(s, o);
- }
- }
-
- /**
- * Return true if the specified record protocol version is out of the
- * range of the possible supported versions.
- */
- static void checkRecordVersion(ProtocolVersion version,
- boolean allowSSL20Hello) throws SSLException {
- // Check if the record version is too old (currently not possible)
- // or if the major version does not match.
- //
- // The actual version negotiation is in the handshaker classes
- if ((version.v < ProtocolVersion.MIN.v) ||
- ((version.major & 0xFF) > (ProtocolVersion.MAX.major & 0xFF))) {
-
- // if it's not SSLv2, we're out of here.
- if (!allowSSL20Hello ||
- (version.v != ProtocolVersion.SSL20Hello.v)) {
- throw new SSLException("Unsupported record version " + version);
- }
- }
- }
-
- /**
- * Read a SSL/TLS record. Throw an IOException if the format is invalid.
- */
- private void readV3Record(InputStream s, OutputStream o)
- throws IOException {
- ProtocolVersion recordVersion = ProtocolVersion.valueOf(buf[1], buf[2]);
-
- // check the record version
- checkRecordVersion(recordVersion, false);
-
- /*
- * Get and check length, then the data.
- */
- int contentLen = ((buf[3] & 0x0ff) << 8) + (buf[4] & 0xff);
-
- /*
- * Check for upper bound.
- */
- if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {
- throw new SSLProtocolException("Bad InputRecord size"
- + ", count = " + contentLen
- + ", buf.length = " + buf.length);
- }
-
- /*
- * Grow "buf" if needed. Since buf is maxRecordSize by default,
- * this only occurs when we receive records which violate the
- * SSL specification. This is a workaround for a Microsoft SSL bug.
- */
- if (contentLen > buf.length - headerSize) {
- byte[] newbuf = new byte[contentLen + headerSize];
- System.arraycopy(buf, 0, newbuf, 0, headerSize);
- buf = newbuf;
- }
+}
- if (exlen < contentLen + headerSize) {
- int really = readFully(
- s, buf, exlen, contentLen + headerSize - exlen);
- if (really < 0) {
- throw new SSLException("SSL peer shut down incorrectly");
- }
- }
-
- // now we've got a complete record.
- count = contentLen + headerSize;
- exlen = 0;
-
- if (debug != null && Debug.isOn("record")) {
- if (count < 0 || count > (maxRecordSize - headerSize)) {
- System.out.println(Thread.currentThread().getName()
- + ", Bad InputRecord size" + ", count = " + count);
- }
- System.out.println(Thread.currentThread().getName()
- + ", READ: " + recordVersion + " "
- + contentName(contentType()) + ", length = " + available());
- }
- /*
- * then caller decrypts, verifies, and uncompresses
- */
- }
-
- /**
- * Deal with unknown records. Called if the first data we read on this
- * connection does not look like an SSL/TLS record. It could a SSLv2
- * message, or just garbage.
- */
- private void handleUnknownRecord(InputStream s, OutputStream o)
- throws IOException {
- /*
- * No? Oh well; does it look like a V2 "ClientHello"?
- * That'd be an unpadded handshake message; we don't
- * bother checking length just now.
- */
- if (((buf[0] & 0x080) != 0) && buf[2] == 1) {
- /*
- * if the user has disabled SSLv2Hello (using
- * setEnabledProtocol) then throw an
- * exception
- */
- if (helloVersion != ProtocolVersion.SSL20Hello) {
- throw new SSLHandshakeException("SSLv2Hello is disabled");
- }
-
- ProtocolVersion recordVersion =
- ProtocolVersion.valueOf(buf[3], buf[4]);
-
- if (recordVersion == ProtocolVersion.SSL20Hello) {
- /*
- * Looks like a V2 client hello, but not one saying
- * "let's talk SSLv3". So we send an SSLv2 error
- * message, one that's treated as fatal by clients.
- * (Otherwise we'll hang.)
- */
- try {
- writeBuffer(o, v2NoCipher, 0, v2NoCipher.length);
- } catch (Exception e) {
- /* NOTHING */
- }
- throw new SSLException("Unsupported SSL v2.0 ClientHello");
- }
-
- /*
- * If we can map this into a V3 ClientHello, read and
- * hash the rest of the V2 handshake, turn it into a
- * V3 ClientHello message, and pass it up.
- */
- int len = ((buf[0] & 0x7f) << 8) +
- (buf[1] & 0xff) - 3;
- if (v2Buf == null) {
- v2Buf = new byte[len];
- }
- if (exlen < len + headerSize) {
- int really = readFully(
- s, v2Buf, exlen - headerSize, len + headerSize - exlen);
- if (really < 0) {
- throw new EOFException("SSL peer shut down incorrectly");
- }
- }
-
- // now we've got a complete record.
- exlen = 0;
-
- hashInternal(buf, 2, 3);
- hashInternal(v2Buf, 0, len);
- V2toV3ClientHello(v2Buf);
- v2Buf = null;
- lastHashed = count;
-
- if (debug != null && Debug.isOn("record")) {
- System.out.println(
- Thread.currentThread().getName()
- + ", READ: SSL v2, contentType = "
- + contentName(contentType())
- + ", translated length = " + available());
- }
- return;
-
- } else {
- /*
- * Does it look like a V2 "ServerHello"?
- */
- if (((buf [0] & 0x080) != 0) && buf [2] == 4) {
- throw new SSLException(
- "SSL V2.0 servers are not supported.");
- }
-
- /*
- * If this is a V2 NoCipher message then this means
- * the other server doesn't support V3. Otherwise, we just
- * don't understand what it's saying.
- */
- for (int i = 0; i < v2NoCipher.length; i++) {
- if (buf[i] != v2NoCipher[i]) {
- throw new SSLException(
- "Unrecognized SSL message, plaintext connection?");
- }
- }
-
- throw new SSLException("SSL V2.0 servers are not supported.");
- }
- }
-
- /*
- * Actually do the write here. For SSLEngine's HS data,
- * we'll override this method and let it take the appropriate
- * action.
- */
- void writeBuffer(OutputStream s, byte [] buf, int off, int len)
- throws IOException {
- s.write(buf, 0, len);
- s.flush();
- }
-
- /*
- * Support "old" clients which are capable of SSL V3.0 protocol ... for
- * example, Navigator 3.0 clients. The V2 message is in the header and
- * the bytes passed as parameter. This routine translates the V2 message
- * into an equivalent V3 one.
- */
- private void V2toV3ClientHello(byte v2Msg []) throws SSLException
- {
- int i;
-
- /*
- * Build the first part of the V3 record header from the V2 one
- * that's now buffered up. (Lengths are fixed up later).
- */
- buf [0] = ct_handshake;
- buf [1] = buf [3]; // V3.x
- buf[2] = buf[4];
- // header [3..4] for handshake message length
- // count = 5;
-
- /*
- * Store the generic V3 handshake header: 4 bytes
- */
- buf [5] = 1; // HandshakeMessage.ht_client_hello
- // buf [6..8] for length of ClientHello (int24)
- // count += 4;
-
- /*
- * ClientHello header starts with SSL version
- */
- buf [9] = buf [1];
- buf [10] = buf [2];
- // count += 2;
- count = 11;
-
- /*
- * Start parsing the V2 message ...
- */
- int cipherSpecLen, sessionIdLen, nonceLen;
-
- cipherSpecLen = ((v2Msg [0] & 0xff) << 8) + (v2Msg [1] & 0xff);
- sessionIdLen = ((v2Msg [2] & 0xff) << 8) + (v2Msg [3] & 0xff);
- nonceLen = ((v2Msg [4] & 0xff) << 8) + (v2Msg [5] & 0xff);
-
- /*
- * Copy Random value/nonce ... if less than the 32 bytes of
- * a V3 "Random", right justify and zero pad to the left. Else
- * just take the last 32 bytes.
- */
- int offset = 6 + cipherSpecLen + sessionIdLen;
-
- if (nonceLen < 32) {
- for (i = 0; i < (32 - nonceLen); i++)
- buf [count++] = 0;
- System.arraycopy(v2Msg, offset, buf, count, nonceLen);
- count += nonceLen;
- } else {
- System.arraycopy(v2Msg, offset + (nonceLen - 32),
- buf, count, 32);
- count += 32;
- }
-
- /*
- * Copy Session ID (only one byte length!)
- */
- offset -= sessionIdLen;
- buf [count++] = (byte) sessionIdLen;
-
- System.arraycopy(v2Msg, offset, buf, count, sessionIdLen);
- count += sessionIdLen;
-
- /*
- * Copy and translate cipher suites ... V2 specs with first byte zero
- * are really V3 specs (in the last 2 bytes), just copy those and drop
- * the other ones. Preference order remains unchanged.
- *
- * Example: Netscape Navigator 3.0 (exportable) says:
- *
- * 0/3, SSL_RSA_EXPORT_WITH_RC4_40_MD5
- * 0/6, SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5
- *
- * Microsoft Internet Explorer 3.0 (exportable) supports only
- *
- * 0/3, SSL_RSA_EXPORT_WITH_RC4_40_MD5
- */
- int j;
-
- offset -= cipherSpecLen;
- j = count + 2;
-
- for (i = 0; i < cipherSpecLen; i += 3) {
- if (v2Msg [offset + i] != 0)
- continue;
- buf [j++] = v2Msg [offset + i + 1];
- buf [j++] = v2Msg [offset + i + 2];
- }
-
- j -= count + 2;
- buf [count++] = (byte) (j >>> 8);
- buf [count++] = (byte) j;
- count += j;
-
- /*
- * Append compression methods (default/null only)
- */
- buf [count++] = 1;
- buf [count++] = 0; // Session.compression_null
-
- /*
- * Fill in lengths of the messages we synthesized (nested:
- * V3 handshake message within V3 record) and then return
- */
- buf [3] = (byte) (count - headerSize);
- buf [4] = (byte) ((count - headerSize) >>> 8);
-
- buf [headerSize + 1] = 0;
- buf [headerSize + 2] = (byte) (((count - headerSize) - 4) >>> 8);
- buf [headerSize + 3] = (byte) ((count - headerSize) - 4);
-
- pos = headerSize;
- }
-
- /**
- * Return a description for the given content type. This method should be
- * in Record, but since that is an interface this is not possible.
- * Called from InputRecord and OutputRecord.
- */
- static String contentName(int contentType) {
- switch (contentType) {
- case ct_change_cipher_spec:
- return "Change Cipher Spec";
- case ct_alert:
- return "Alert";
- case ct_handshake:
- return "Handshake";
- case ct_application_data:
- return "Application Data";
- default:
- return "contentType = " + contentType;
- }
- }
-
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java Thu Jun 04 18:49:37 2015 -0700
@@ -64,24 +64,9 @@
// If true, then all the Kerberos-based crypto we need is available.
private final static boolean kerberosAvailable;
static {
- boolean temp;
- try {
- AccessController.doPrivileged(
- new PrivilegedExceptionAction<Void>() {
- @Override
- public Void run() throws Exception {
- // Test for Kerberos using the bootstrap class loader
- Class.forName("sun.security.krb5.PrincipalName", true,
- null);
- return null;
- }
- });
- temp = true;
-
- } catch (Exception e) {
- temp = false;
- }
- kerberosAvailable = temp;
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find("KRB5");
+ kerberosAvailable = (p != null);
}
static {
--- a/jdk/src/java.base/share/classes/sun/security/ssl/KerberosClientKeyExchange.java Thu Jun 04 09:31:49 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl;
-
-import java.io.IOException;
-import java.io.PrintStream;
-import java.security.AccessController;
-import java.security.AccessControlContext;
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import java.security.SecureRandom;
-import javax.crypto.SecretKey;
-
-/**
- * A helper class that calls the KerberosClientKeyExchange implementation.
- */
-public class KerberosClientKeyExchange extends HandshakeMessage {
-
- private static final String IMPL_CLASS =
- "sun.security.ssl.krb5.KerberosClientKeyExchangeImpl";
-
- private static final Class<?> implClass = AccessController.doPrivileged(
- new PrivilegedAction<Class<?>>() {
- @Override
- public Class<?> run() {
- try {
- return Class.forName(IMPL_CLASS, true, null);
- } catch (ClassNotFoundException cnf) {
- return null;
- }
- }
- }
- );
- private final KerberosClientKeyExchange impl = createImpl();
-
- private KerberosClientKeyExchange createImpl() {
- if (implClass != null &&
- getClass() == KerberosClientKeyExchange.class) {
- try {
- return (KerberosClientKeyExchange)implClass.newInstance();
- } catch (InstantiationException e) {
- throw new AssertionError(e);
- } catch (IllegalAccessException e) {
- throw new AssertionError(e);
- }
- }
- return null;
- }
-
- // This constructor will be called when constructing an instance of its
- // subclass -- KerberosClientKeyExchangeImpl. Please won't check the
- // value of impl variable in this constructor.
- protected KerberosClientKeyExchange() {
- // please won't check the value of impl variable
- }
-
- public KerberosClientKeyExchange(String serverName,
- AccessControlContext acc, ProtocolVersion protocolVersion,
- SecureRandom rand) throws IOException {
-
- if (impl != null) {
- init(serverName, acc, protocolVersion, rand);
- } else {
- throw new IllegalStateException("Kerberos is unavailable");
- }
- }
-
- public KerberosClientKeyExchange(ProtocolVersion protocolVersion,
- ProtocolVersion clientVersion, SecureRandom rand,
- HandshakeInStream input, AccessControlContext acc,
- Object serverKeys) throws IOException {
-
- if (impl != null) {
- init(protocolVersion, clientVersion, rand, input, acc, serverKeys);
- } else {
- throw new IllegalStateException("Kerberos is unavailable");
- }
- }
-
- @Override
- int messageType() {
- return ht_client_key_exchange;
- }
-
- @Override
- public int messageLength() {
- return impl.messageLength();
- }
-
- @Override
- public void send(HandshakeOutStream s) throws IOException {
- impl.send(s);
- }
-
- @Override
- public void print(PrintStream p) throws IOException {
- impl.print(p);
- }
-
- public void init(String serverName,
- AccessControlContext acc, ProtocolVersion protocolVersion,
- SecureRandom rand) throws IOException {
-
- if (impl != null) {
- impl.init(serverName, acc, protocolVersion, rand);
- }
- }
-
- public void init(ProtocolVersion protocolVersion,
- ProtocolVersion clientVersion, SecureRandom rand,
- HandshakeInStream input, AccessControlContext acc,
- Object ServiceCreds) throws IOException {
-
- if (impl != null) {
- impl.init(protocolVersion, clientVersion,
- rand, input, acc, ServiceCreds);
- }
- }
-
- public byte[] getUnencryptedPreMasterSecret() {
- return impl.getUnencryptedPreMasterSecret();
- }
-
- public Principal getPeerPrincipal(){
- return impl.getPeerPrincipal();
- }
-
- public Principal getLocalPrincipal(){
- return impl.getLocalPrincipal();
- }
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Krb5Helper.java Thu Jun 04 09:31:49 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl;
-
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.Permission;
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import javax.crypto.SecretKey;
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginException;
-
-/**
- * A helper class for Kerberos APIs.
- */
-public final class Krb5Helper {
-
- private Krb5Helper() { }
-
- // loads Krb5Proxy implementation class if available
- private static final String IMPL_CLASS =
- "sun.security.ssl.krb5.Krb5ProxyImpl";
-
- private static final Krb5Proxy proxy =
- AccessController.doPrivileged(new PrivilegedAction<Krb5Proxy>() {
- @Override
- public Krb5Proxy run() {
- try {
- Class<?> c = Class.forName(IMPL_CLASS, true, null);
- return (Krb5Proxy)c.newInstance();
- } catch (ClassNotFoundException cnf) {
- return null;
- } catch (InstantiationException e) {
- throw new AssertionError(e);
- } catch (IllegalAccessException e) {
- throw new AssertionError(e);
- }
- }});
-
- /**
- * Returns true if Kerberos is available.
- */
- public static boolean isAvailable() {
- return proxy != null;
- }
-
- private static void ensureAvailable() {
- if (proxy == null)
- throw new AssertionError("Kerberos should have been available");
- }
-
- /**
- * Returns the Subject associated with client-side of the SSL socket.
- */
- public static Subject getClientSubject(AccessControlContext acc)
- throws LoginException {
- ensureAvailable();
- return proxy.getClientSubject(acc);
- }
-
- /**
- * Returns the Subject associated with server-side of the SSL socket.
- */
- public static Subject getServerSubject(AccessControlContext acc)
- throws LoginException {
- ensureAvailable();
- return proxy.getServerSubject(acc);
- }
-
- /**
- * Returns the KerberosKeys for the default server-side principal.
- */
- public static Object getServiceCreds(AccessControlContext acc)
- throws LoginException {
- ensureAvailable();
- return proxy.getServiceCreds(acc);
- }
-
- /**
- * Returns the server-side principal name associated with the KerberosKey.
- */
- public static String getServerPrincipalName(Object serviceCreds) {
- ensureAvailable();
- return proxy.getServerPrincipalName(serviceCreds);
- }
-
- /**
- * Returns the hostname embedded in the principal name.
- */
- public static String getPrincipalHostName(Principal principal) {
- ensureAvailable();
- return proxy.getPrincipalHostName(principal);
- }
-
- /**
- * Returns a ServicePermission for the principal name and action.
- */
- public static Permission getServicePermission(String principalName,
- String action) {
- ensureAvailable();
- return proxy.getServicePermission(principalName, action);
- }
-
- /**
- * Determines if the Subject might contain creds for princ.
- */
- public static boolean isRelated(Subject subject, Principal princ) {
- ensureAvailable();
- return proxy.isRelated(subject, princ);
- }
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Krb5Proxy.java Thu Jun 04 09:31:49 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl;
-
-import java.security.AccessControlContext;
-import java.security.Permission;
-import java.security.Principal;
-import javax.crypto.SecretKey;
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginException;
-
-/**
- * An interface to a subset of the Kerberos APIs to avoid a static dependency
- * on the types defined by these APIs.
- */
-public interface Krb5Proxy {
-
- /**
- * Returns the Subject associated with the client-side of the SSL socket.
- */
- Subject getClientSubject(AccessControlContext acc) throws LoginException;
-
- /**
- * Returns the Subject associated with the server-side of the SSL socket.
- */
- Subject getServerSubject(AccessControlContext acc) throws LoginException;
-
-
- /**
- * Returns the Kerberos ServiceCreds for the default server-side principal.
- */
- Object getServiceCreds(AccessControlContext acc) throws LoginException;
-
- /**
- * Returns the server-side principal name associated with the KerberosKey.
- */
- String getServerPrincipalName(Object serviceCreds);
-
- /**
- * Returns the hostname embedded in the principal name.
- */
- String getPrincipalHostName(Principal principal);
-
- /**
- * Returns a ServicePermission for the principal name and action.
- */
- Permission getServicePermission(String principalName, String action);
-
- /**
- * Determines if the Subject might contain creds for princ.
- */
- boolean isRelated(Subject subject, Principal princ);
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/MAC.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/MAC.java Thu Jun 04 18:49:37 2015 -0700
@@ -23,7 +23,6 @@
* questions.
*/
-
package sun.security.ssl;
import java.security.InvalidKeyException;
@@ -50,24 +49,26 @@
*/
final class MAC extends Authenticator {
- final static MAC NULL = new MAC();
+ final static MAC TLS_NULL = new MAC(false);
// Value of the null MAC is fixed
private static final byte nullMAC[] = new byte[0];
// internal identifier for the MAC algorithm
- private final MacAlg macAlg;
+ private final MacAlg macAlg;
// JCE Mac object
private final Mac mac;
- private MAC() {
+ MAC(boolean isDTLS) {
+ super(isDTLS);
+
macAlg = M_NULL;
mac = null;
}
/**
- * Set up, configured for the given SSL/TLS MAC type and version.
+ * Set up, configured for the given MAC type and version.
*/
MAC(MacAlg macAlg, ProtocolVersion protocolVersion, SecretKey key)
throws NoSuchAlgorithmException, InvalidKeyException {
@@ -75,12 +76,14 @@
this.macAlg = macAlg;
String algorithm;
- boolean tls = (protocolVersion.v >= ProtocolVersion.TLS10.v);
+
+ // using SSL MAC computation?
+ boolean useSSLMac = (protocolVersion.v < ProtocolVersion.TLS10.v);
if (macAlg == M_MD5) {
- algorithm = tls ? "HmacMD5" : "SslMacMD5";
+ algorithm = useSSLMac ? "SslMacMD5" : "HmacMD5";
} else if (macAlg == M_SHA) {
- algorithm = tls ? "HmacSHA1" : "SslMacSHA1";
+ algorithm = useSSLMac ? "SslMacSHA1" : "HmacSHA1";
} else if (macAlg == M_SHA256) {
algorithm = "HmacSHA256"; // TLS 1.2+
} else if (macAlg == M_SHA384) {
@@ -122,6 +125,8 @@
* @param offset start of compressed record data
* @param len the size of the compressed record
* @param isSimulated if true, simulate the MAC computation
+ *
+ * @return the MAC result
*/
final byte[] compute(byte type, byte buf[],
int offset, int len, boolean isSimulated) {
@@ -130,7 +135,8 @@
}
if (!isSimulated) {
- byte[] additional = acquireAuthenticationBytes(type, len);
+ // Uses the implicit sequence number for the computation.
+ byte[] additional = acquireAuthenticationBytes(type, len, null);
mac.update(additional);
}
mac.update(buf, offset, len);
@@ -149,15 +155,22 @@
* @param bb a ByteBuffer in which the position and limit
* demarcate the data to be MAC'd.
* @param isSimulated if true, simulate the MAC computation
+ * @param sequence the explicit sequence number, or null if using
+ * the implicit sequence number for the computation
+ *
+ * @return the MAC result
*/
- final byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) {
+ final byte[] compute(byte type, ByteBuffer bb,
+ byte[] sequence, boolean isSimulated) {
+
if (macAlg.size == 0) {
return nullMAC;
}
if (!isSimulated) {
+ // Uses the explicit sequence number for the computation.
byte[] additional =
- acquireAuthenticationBytes(type, bb.remaining());
+ acquireAuthenticationBytes(type, bb.remaining(), sequence);
mac.update(additional);
}
mac.update(bb);
@@ -165,5 +178,22 @@
return mac.doFinal();
}
+ /**
+ * Compute and returns the MAC for the remaining data
+ * in this ByteBuffer.
+ *
+ * On return, the bb position == limit, and limit will
+ * have not changed.
+ *
+ * @param type record type
+ * @param bb a ByteBuffer in which the position and limit
+ * demarcate the data to be MAC'd.
+ * @param isSimulated if true, simulate the the MAC computation
+ *
+ * @return the MAC result
+ */
+ final byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) {
+ // Uses the implicit sequence number for the computation.
+ return compute(type, bb, null, isSimulated);
+ }
}
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/MaxFragmentLengthExtension.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+import javax.net.ssl.SSLProtocolException;
+
+/*
+ * [RFC6066] TLS specifies a fixed maximum plaintext fragment length of
+ * 2^14 bytes. It may be desirable for constrained clients to negotiate
+ * a smaller maximum fragment length due to memory limitations or bandwidth
+ * limitations.
+ *
+ * In order to negotiate smaller maximum fragment lengths, clients MAY
+ * include an extension of type "max_fragment_length" in the (extended)
+ * client hello. The "extension_data" field of this extension SHALL
+ * contain:
+ *
+ * enum{
+ * 2^9(1), 2^10(2), 2^11(3), 2^12(4), (255)
+ * } MaxFragmentLength;
+ *
+ * whose value is the desired maximum fragment length.
+ */
+final class MaxFragmentLengthExtension extends HelloExtension {
+
+ private static final int MAX_FRAGMENT_LENGTH_512 = 1; // 2^9
+ private static final int MAX_FRAGMENT_LENGTH_1024 = 2; // 2^10
+ private static final int MAX_FRAGMENT_LENGTH_2048 = 3; // 2^11
+ private static final int MAX_FRAGMENT_LENGTH_4096 = 4; // 2^12
+
+ final int maxFragmentLength;
+
+ MaxFragmentLengthExtension(int fragmentSize) {
+ super(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
+
+ if (fragmentSize < 1024) {
+ maxFragmentLength = MAX_FRAGMENT_LENGTH_512;
+ } else if (fragmentSize < 2048) {
+ maxFragmentLength = MAX_FRAGMENT_LENGTH_1024;
+ } else if (fragmentSize < 4096) {
+ maxFragmentLength = MAX_FRAGMENT_LENGTH_2048;
+ } else {
+ maxFragmentLength = MAX_FRAGMENT_LENGTH_4096;
+ }
+ }
+
+ MaxFragmentLengthExtension(HandshakeInStream s, int len)
+ throws IOException {
+ super(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
+
+ // check the extension length
+ if (len != 1) {
+ throw new SSLProtocolException("Invalid " + type + " extension");
+ }
+
+ maxFragmentLength = s.getInt8();
+ if ((maxFragmentLength > 4) || (maxFragmentLength < 1)) {
+ throw new SSLProtocolException("Invalid " + type + " extension");
+ }
+ }
+
+ // Length of the encoded extension, including the type and length fields
+ @Override
+ int length() {
+ return 5; // 4: extension type and length fields
+ // 1: MaxFragmentLength field
+ }
+
+ @Override
+ void send(HandshakeOutStream s) throws IOException {
+ s.putInt16(type.id);
+ s.putInt16(1);
+ s.putInt8(maxFragmentLength);
+ }
+
+ int getMaxFragLen() {
+ switch (maxFragmentLength) {
+ case MAX_FRAGMENT_LENGTH_512:
+ return 512;
+ case MAX_FRAGMENT_LENGTH_1024:
+ return 1024;
+ case MAX_FRAGMENT_LENGTH_2048:
+ return 2048;
+ case MAX_FRAGMENT_LENGTH_4096:
+ return 4096;
+ }
+
+ // unlikely to happen
+ return -1;
+ }
+
+ static boolean needFragLenNego(int fragmentSize) {
+ return (fragmentSize > 0) && (fragmentSize <= 4096);
+ }
+
+ static int getValidMaxFragLen(int fragmentSize) {
+ if (fragmentSize < 1024) {
+ return 512;
+ } else if (fragmentSize < 2048) {
+ return 1024;
+ } else if (fragmentSize < 4096) {
+ return 2048;
+ } else if (fragmentSize == 4096) {
+ return 4096;
+ } else {
+ return 16384;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Extension " + type + ", max_fragment_length: " +
+ "(2^" + (maxFragmentLength + 8) + ")";
+ }
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/OutputRecord.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/OutputRecord.java Thu Jun 04 18:49:37 2015 -0700
@@ -23,7 +23,6 @@
* questions.
*/
-
package sun.security.ssl;
import java.io.*;
@@ -35,92 +34,61 @@
/**
- * SSL 3.0 records, as written to a TCP stream.
- *
- * Each record has a message area that starts out with data supplied by the
- * application. It may grow/shrink due to compression and will be modified
- * in place for mac-ing and encryption.
- *
- * Handshake records have additional needs, notably accumulation of a set
- * of hashes which are used to establish that handshaking was done right.
- * Handshake records usually have several handshake messages each, and we
- * need message-level control over what's hashed.
+ * {@code OutputRecord} takes care of the management of SSL/TLS/DTLS output
+ * records, including buffering, encryption, handshake messages marshal, etc.
*
* @author David Brownell
*/
-class OutputRecord extends ByteArrayOutputStream implements Record {
+abstract class OutputRecord extends ByteArrayOutputStream
+ implements Record, Closeable {
+
+ /* Class and subclass dynamic debugging support */
+ static final Debug debug = Debug.getInstance("ssl");
- private HandshakeHash handshakeHash;
- private int lastHashed;
- private boolean firstMessage;
- final private byte contentType;
- private int headerOffset;
+ Authenticator writeAuthenticator;
+ CipherBox writeCipher;
+
+ HandshakeHash handshakeHash;
+ boolean firstMessage;
// current protocol version, sent as record version
- ProtocolVersion protocolVersion;
+ ProtocolVersion protocolVersion;
// version for the ClientHello message. Only relevant if this is a
// client handshake record. If set to ProtocolVersion.SSL20Hello,
// the V3 client hello is converted to V2 format.
- private ProtocolVersion helloVersion;
+ ProtocolVersion helloVersion;
+
+ // Is it the first application record to write?
+ boolean isFirstAppOutputRecord = true;
- /* Class and subclass dynamic debugging support */
- static final Debug debug = Debug.getInstance("ssl");
+ // packet size
+ int packetSize;
+
+ // fragment size
+ int fragmentSize;
+
+ // closed or not?
+ boolean isClosed;
/*
- * Default constructor makes a record supporting the maximum
- * SSL record size. It allocates the header bytes directly.
- *
- * The structure of the byte buffer looks like:
- *
- * |---------+--------+-------+---------------------------------|
- * | unused | header | IV | content, MAC/TAG, padding, etc. |
- * | headerPlusMaxIVSize |
- *
- * unused: unused part of the buffer of size
- *
- * headerPlusMaxIVSize - header size - IV size
- *
- * When this object is created, we don't know the protocol
- * version number, IV length, etc., so reserve space in front
- * to avoid extra data movement (copies).
- * header: the header of an SSL record
- * IV: the optional IV/nonce field, it is only required for block
- * (TLS 1.1 or later) and AEAD cipher suites.
- *
- * @param type the content type for the record
+ * Mappings from V3 cipher suite encodings to their pure V2 equivalents.
+ * This is taken from the SSL V3 specification, Appendix E.
*/
- OutputRecord(byte type, int size) {
- super(size);
- this.protocolVersion = ProtocolVersion.DEFAULT;
- this.helloVersion = ProtocolVersion.DEFAULT_HELLO;
- firstMessage = true;
- count = headerPlusMaxIVSize;
- contentType = type;
- lastHashed = count;
- headerOffset = headerPlusMaxIVSize - headerSize;
+ private static int[] V3toV2CipherMap1 =
+ {-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
+ private static int[] V3toV2CipherMap3 =
+ {-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
+
+ OutputRecord() {
+ this.writeCipher = CipherBox.NULL;
+ this.firstMessage = true;
+ this.fragmentSize = Record.maxDataSize;
+
+ // Please set packetSize and protocolVersion in the implementation.
}
- OutputRecord(byte type) {
- this(type, recordSize(type));
- }
-
- /**
- * Get the size of the buffer we need for records of the specified
- * type.
- */
- private static int recordSize(byte type) {
- if ((type == ct_change_cipher_spec) || (type == ct_alert)) {
- return maxAlertRecordSize;
- } else {
- return maxRecordSize;
- }
- }
-
- /*
- * Updates the SSL version of this record.
- */
- synchronized void setVersion(ProtocolVersion protocolVersion) {
+ void setVersion(ProtocolVersion protocolVersion) {
this.protocolVersion = protocolVersion;
}
@@ -132,441 +100,364 @@
}
/*
- * Reset the record so that it can be refilled, starting
- * immediately after the header.
- */
- @Override
- public synchronized void reset() {
- super.reset();
- count = headerPlusMaxIVSize;
- lastHashed = count;
- headerOffset = headerPlusMaxIVSize - headerSize;
- }
-
- /*
* For handshaking, we need to be able to hash every byte above the
* record marking layer. This is where we're guaranteed to see those
* bytes, so this is where we can hash them.
*/
void setHandshakeHash(HandshakeHash handshakeHash) {
- assert(contentType == ct_handshake);
this.handshakeHash = handshakeHash;
}
/*
- * We hash (the plaintext) on demand. There is one place where
- * we want to access the hash in the middle of a record: client
- * cert message gets hashed, and part of the same record is the
- * client cert verify message which uses that hash. So we track
- * how much of each record we've hashed so far.
- */
- void doHashes() {
- int len = count - lastHashed;
-
- if (len > 0) {
- hashInternal(buf, lastHashed, len);
- lastHashed = count;
- }
- }
-
- /*
- * Need a helper function so we can hash the V2 hello correctly
- */
- private void hashInternal(byte buf [], int offset, int len) {
- if (debug != null && Debug.isOn("data")) {
- try {
- HexDumpEncoder hd = new HexDumpEncoder();
-
- System.out.println("[write] MD5 and SHA1 hashes: len = "
- + len);
- hd.encodeBuffer(new ByteArrayInputStream(buf,
- lastHashed, len), System.out);
- } catch (IOException e) { }
- }
-
- handshakeHash.update(buf, lastHashed, len);
- lastHashed = count;
- }
-
- /*
* Return true iff the record is empty -- to avoid doing the work
* of sending empty records over the network.
*/
boolean isEmpty() {
- return count == headerPlusMaxIVSize;
- }
-
- /*
- * Return true if the record is of an alert of the given description.
- *
- * Per SSL/TLS specifications, alert messages convey the severity of the
- * message (warning or fatal) and a description of the alert. An alert
- * is defined with a two bytes struct, {byte level, byte description},
- * following after the header bytes.
- */
- boolean isAlert(byte description) {
- if ((count > (headerPlusMaxIVSize + 1)) && (contentType == ct_alert)) {
- return buf[headerPlusMaxIVSize + 1] == description;
- }
-
return false;
}
- /*
- * Encrypt ... length may grow due to block cipher padding, or
- * message authentication code or tag.
- */
- void encrypt(Authenticator authenticator, CipherBox box)
- throws IOException {
+ boolean seqNumIsHuge() {
+ return (writeAuthenticator != null) &&
+ writeAuthenticator.seqNumIsHuge();
+ }
- // In case we are automatically flushing a handshake stream, make
- // sure we have hashed the message first.
- //
- // when we support compression, hashing can't go here
- // since it'll need to be done on the uncompressed data,
- // and the MAC applies to the compressed data.
- if (contentType == ct_handshake) {
- doHashes();
- }
+ // SSLEngine and SSLSocket
+ abstract void encodeAlert(byte level, byte description) throws IOException;
+
+ // SSLEngine and SSLSocket
+ abstract void encodeHandshake(byte[] buffer,
+ int offset, int length) throws IOException;
+
+ // SSLEngine and SSLSocket
+ abstract void encodeChangeCipherSpec() throws IOException;
- // Requires message authentication code for stream and block
- // cipher suites.
- if (authenticator instanceof MAC) {
- MAC signer = (MAC)authenticator;
- if (signer.MAClen() != 0) {
- byte[] hash = signer.compute(contentType, buf,
- headerPlusMaxIVSize, count - headerPlusMaxIVSize, false);
- write(hash);
- }
- }
+ // apply to SSLEngine only
+ Ciphertext encode(ByteBuffer[] sources, int offset, int length,
+ ByteBuffer destination) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ // apply to SSLEngine only
+ void encodeV2NoCipher() throws IOException {
+ throw new UnsupportedOperationException();
+ }
- if (!box.isNullCipher()) {
- // Requires explicit IV/nonce for CBC/AEAD cipher suites for
- // TLS 1.1 or later.
- if ((protocolVersion.v >= ProtocolVersion.TLS11.v) &&
- (box.isCBCMode() || box.isAEADMode())) {
- byte[] nonce = box.createExplicitNonce(authenticator,
- contentType, count - headerPlusMaxIVSize);
- int offset = headerPlusMaxIVSize - nonce.length;
- System.arraycopy(nonce, 0, buf, offset, nonce.length);
- headerOffset = offset - headerSize;
- } else {
- headerOffset = headerPlusMaxIVSize - headerSize;
- }
+ // apply to SSLSocket only
+ void deliver(byte[] source, int offset, int length) throws IOException {
+ throw new UnsupportedOperationException();
+ }
- // encrypt the content
- int offset = headerPlusMaxIVSize;
- if (!box.isAEADMode()) {
- // The explicit IV can be encrypted.
- offset = headerOffset + headerSize;
- } // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
+ // apply to SSLSocket only
+ void setDeliverStream(OutputStream outputStream) {
+ throw new UnsupportedOperationException();
+ }
- count = offset + box.encrypt(buf, offset, count - offset);
- }
+ // apply to SSLEngine only
+ Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
+ throw new UnsupportedOperationException();
}
- /*
- * Tell how full the buffer is ... for filling it with application or
- * handshake data.
- */
- final int availableDataBytes() {
- int dataSize = count - headerPlusMaxIVSize;
- return maxDataSize - dataSize;
+ void changeWriteCiphers(Authenticator writeAuthenticator,
+ CipherBox writeCipher) throws IOException {
+
+ encodeChangeCipherSpec();
+
+ /*
+ * Dispose of any intermediate state in the underlying cipher.
+ * For PKCS11 ciphers, this will release any attached sessions,
+ * and thus make finalization faster.
+ *
+ * Since MAC's doFinal() is called for every SSL/TLS packet, it's
+ * not necessary to do the same with MAC's.
+ */
+ writeCipher.dispose();
+
+ this.writeAuthenticator = writeAuthenticator;
+ this.writeCipher = writeCipher;
+ this.isFirstAppOutputRecord = true;
}
- /*
- * Increases the capacity if necessary to ensure that it can hold
- * at least the number of elements specified by the minimum
- * capacity argument.
- *
- * Note that the increased capacity is only can be used for held
- * record buffer. Please DO NOT update the availableDataBytes()
- * according to the expended buffer capacity.
- *
- * @see availableDataBytes()
- */
- private void ensureCapacity(int minCapacity) {
- // overflow-conscious code
- if (minCapacity > buf.length) {
- buf = Arrays.copyOf(buf, minCapacity);
+ void changePacketSize(int packetSize) {
+ this.packetSize = packetSize;
+ }
+
+ void changeFragmentSize(int fragmentSize) {
+ this.fragmentSize = fragmentSize;
+ }
+
+ int getMaxPacketSize() {
+ return packetSize;
+ }
+
+ // apply to DTLS SSLEngine
+ void initHandshaker() {
+ // blank
+ }
+
+ @Override
+ synchronized public void close() throws IOException {
+ if (!isClosed) {
+ isClosed = true;
+ writeCipher.dispose();
}
}
- /*
- * Return the type of SSL record that's buffered here.
- */
- final byte contentType() {
- return contentType;
- }
+ //
+ // shared helpers
+ //
+
+ // Encrypt a fragment and wrap up a record.
+ //
+ // To be consistent with the spec of SSLEngine.wrap() methods, the
+ // destination ByteBuffer's position is updated to reflect the amount
+ // of data produced. The limit remains the same.
+ static long encrypt(Authenticator authenticator,
+ CipherBox encCipher, byte contentType, ByteBuffer destination,
+ int headerOffset, int dstLim, int headerSize,
+ ProtocolVersion protocolVersion, boolean isDTLS) {
+
+ byte[] sequenceNumber = null;
+ int dstContent = destination.position();
- /*
- * Write the record out on the stream. Note that you must have (in
- * order) compressed the data, appended the MAC, and encrypted it in
- * order for the record to be understood by the other end. (Some of
- * those steps will be null early in handshaking.)
- *
- * Note that this does no locking for the connection, it's required
- * that synchronization be done elsewhere. Also, this does its work
- * in a single low level write, for efficiency.
- */
- void write(OutputStream s, boolean holdRecord,
- ByteArrayOutputStream heldRecordBuffer) throws IOException {
+ // Acquire the current sequence number before using.
+ if (isDTLS) {
+ sequenceNumber = authenticator.sequenceNumber();
+ }
+
+ // "flip" but skip over header again, add MAC & encrypt
+ if (authenticator instanceof MAC) {
+ MAC signer = (MAC)authenticator;
+ if (signer.MAClen() != 0) {
+ byte[] hash = signer.compute(contentType, destination, false);
- /*
- * Don't emit content-free records. (Even change cipher spec
- * messages have a byte of data!)
- */
- if (count == headerPlusMaxIVSize) {
- return;
+ /*
+ * position was advanced to limit in MAC compute above.
+ *
+ * Mark next area as writable (above layers should have
+ * established that we have plenty of room), then write
+ * out the hash.
+ */
+ destination.limit(destination.limit() + hash.length);
+ destination.put(hash);
+
+ // reset the position and limit
+ destination.limit(destination.position());
+ destination.position(dstContent);
+ }
}
- int length = count - headerOffset - headerSize;
- // "should" really never write more than about 14 Kb...
- if (length < 0) {
- throw new SSLException("output record size too small: "
- + length);
+ if (!encCipher.isNullCipher()) {
+ if (protocolVersion.useTLS11PlusSpec() &&
+ (encCipher.isCBCMode() || encCipher.isAEADMode())) {
+ byte[] nonce = encCipher.createExplicitNonce(
+ authenticator, contentType, destination.remaining());
+ destination.position(headerOffset + headerSize);
+ destination.put(nonce);
+ }
+ if (!encCipher.isAEADMode()) {
+ // The explicit IV in TLS 1.1 and later can be encrypted.
+ destination.position(headerOffset + headerSize);
+ } // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
+
+ // Encrypt may pad, so again the limit may be changed.
+ encCipher.encrypt(destination, dstLim);
+ } else {
+ destination.position(destination.limit());
+ }
+
+ // Finish out the record header.
+ int fragLen = destination.limit() - headerOffset - headerSize;
+
+ destination.put(headerOffset, contentType); // content type
+ destination.put(headerOffset + 1, protocolVersion.major);
+ destination.put(headerOffset + 2, protocolVersion.minor);
+ if (!isDTLS) {
+ // fragment length
+ destination.put(headerOffset + 3, (byte)(fragLen >> 8));
+ destination.put(headerOffset + 4, (byte)fragLen);
+ } else {
+ // epoch and sequence_number
+ destination.put(headerOffset + 3, sequenceNumber[0]);
+ destination.put(headerOffset + 4, sequenceNumber[1]);
+ destination.put(headerOffset + 5, sequenceNumber[2]);
+ destination.put(headerOffset + 6, sequenceNumber[3]);
+ destination.put(headerOffset + 7, sequenceNumber[4]);
+ destination.put(headerOffset + 8, sequenceNumber[5]);
+ destination.put(headerOffset + 9, sequenceNumber[6]);
+ destination.put(headerOffset + 10, sequenceNumber[7]);
+
+ // fragment length
+ destination.put(headerOffset + 11, (byte)(fragLen >> 8));
+ destination.put(headerOffset + 12, (byte)fragLen);
+
+ // Increase the sequence number for next use.
+ authenticator.increaseSequenceNumber();
}
- if (debug != null
- && (Debug.isOn("record") || Debug.isOn("handshake"))) {
- if ((debug != null && Debug.isOn("record"))
- || contentType() == ct_change_cipher_spec)
- System.out.println(Thread.currentThread().getName()
- // v3.0/v3.1 ...
- + ", WRITE: " + protocolVersion
- + " " + InputRecord.contentName(contentType())
- + ", length = " + length);
+ // Update destination position to reflect the amount of data produced.
+ destination.position(destination.limit());
+
+ return Authenticator.toLong(sequenceNumber);
+ }
+
+ // Encrypt a fragment and wrap up a record.
+ //
+ // Uses the internal expandable buf variable and the current
+ // protocolVersion variable.
+ void encrypt(Authenticator authenticator,
+ CipherBox encCipher, byte contentType, int headerSize) {
+
+ int position = headerSize + writeCipher.getExplicitNonceSize();
+
+ // "flip" but skip over header again, add MAC & encrypt
+ int macLen = 0;
+ if (authenticator instanceof MAC) {
+ MAC signer = (MAC)authenticator;
+ macLen = signer.MAClen();
+ if (macLen != 0) {
+ byte[] hash = signer.compute(contentType,
+ buf, position, (count - position), false);
+
+ write(hash, 0, hash.length);
+ }
}
- /*
- * If this is the initial ClientHello on this connection and
- * we're not trying to resume a (V3) session then send a V2
- * ClientHello instead so we can detect V2 servers cleanly.
- */
- if (firstMessage && useV2Hello()) {
- byte[] v3Msg = new byte[length - 4];
- System.arraycopy(buf, headerPlusMaxIVSize + 4,
- v3Msg, 0, v3Msg.length);
- headerOffset = 0; // reset the header offset
- V3toV2ClientHello(v3Msg);
- handshakeHash.reset();
- lastHashed = 2;
- doHashes();
- if (debug != null && Debug.isOn("record")) {
- System.out.println(
- Thread.currentThread().getName()
- + ", WRITE: SSLv2 client hello message"
- + ", length = " + (count - 2)); // 2 byte SSLv2 header
+ if (!encCipher.isNullCipher()) {
+ // Requires explicit IV/nonce for CBC/AEAD cipher suites for
+ // TLS 1.1 or later.
+ if (protocolVersion.useTLS11PlusSpec() &&
+ (encCipher.isCBCMode() || encCipher.isAEADMode())) {
+
+ byte[] nonce = encCipher.createExplicitNonce(
+ authenticator, contentType, (count - position));
+ int noncePos = position - nonce.length;
+ System.arraycopy(nonce, 0, buf, noncePos, nonce.length);
+ }
+
+ if (!encCipher.isAEADMode()) {
+ // The explicit IV in TLS 1.1 and later can be encrypted.
+ position = headerSize;
+ } // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
+
+ // increase buf capacity if necessary
+ int fragSize = count - position;
+ int packetSize =
+ encCipher.calculatePacketSize(fragSize, macLen, headerSize);
+ if (packetSize > (buf.length - position)) {
+ byte[] newBuf = new byte[position + packetSize];
+ System.arraycopy(buf, 0, newBuf, 0, count);
+ buf = newBuf;
}
- } else {
- /*
- * Fill out the header, write it and the message.
- */
- buf[headerOffset + 0] = contentType;
- buf[headerOffset + 1] = protocolVersion.major;
- buf[headerOffset + 2] = protocolVersion.minor;
- buf[headerOffset + 3] = (byte)(length >> 8);
- buf[headerOffset + 4] = (byte)(length);
+
+ // Encrypt may pad, so again the count may be changed.
+ count = position +
+ encCipher.encrypt(buf, position, (count - position));
}
- firstMessage = false;
+
+ // Fill out the header, write it and the message.
+ int fragLen = count - headerSize;
+ buf[0] = contentType;
+ buf[1] = protocolVersion.major;
+ buf[2] = protocolVersion.minor;
+ buf[3] = (byte)((fragLen >> 8) & 0xFF);
+ buf[4] = (byte)(fragLen & 0xFF);
+ }
+
+ static ByteBuffer encodeV2ClientHello(
+ byte[] fragment, int offset, int length) throws IOException {
+
+ int v3SessIdLenOffset = offset + 34; // 2: client_version
+ // 32: random
+
+ int v3SessIdLen = fragment[v3SessIdLenOffset];
+ int v3CSLenOffset = v3SessIdLenOffset + 1 + v3SessIdLen;
+ int v3CSLen = ((fragment[v3CSLenOffset] & 0xff) << 8) +
+ (fragment[v3CSLenOffset + 1] & 0xff);
+ int cipherSpecs = v3CSLen / 2; // 2: cipher spec size
+
+ // Estimate the max V2ClientHello message length
+ //
+ // 11: header size
+ // (cipherSpecs * 6): cipher_specs
+ // 6: one cipher suite may need 6 bytes, see V3toV2CipherSuite.
+ // 3: placeholder for the TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+ // signaling cipher suite
+ // 32: challenge size
+ int v2MaxMsgLen = 11 + (cipherSpecs * 6) + 3 + 32;
+
+ // Create a ByteBuffer backed by an accessible byte array.
+ byte[] dstBytes = new byte[v2MaxMsgLen];
+ ByteBuffer dstBuf = ByteBuffer.wrap(dstBytes);
/*
- * The upper levels may want us to delay sending this packet so
- * multiple TLS Records can be sent in one (or more) TCP packets.
- * If so, add this packet to the heldRecordBuffer.
- *
- * NOTE: all writes have been synchronized by upper levels.
+ * Copy over the cipher specs. We don't care about actually
+ * translating them for use with an actual V2 server since
+ * we only talk V3. Therefore, just copy over the V3 cipher
+ * spec values with a leading 0.
*/
- int debugOffset = 0;
- if (holdRecord) {
- /*
- * If holdRecord is true, we must have a heldRecordBuffer.
- *
- * Don't worry about the override of writeBuffer(), because
- * when holdRecord is true, the implementation in this class
- * will be used.
- */
- writeBuffer(heldRecordBuffer,
- buf, headerOffset, count - headerOffset, debugOffset);
- } else {
- // It's time to send, do we have buffered data?
- // May or may not have a heldRecordBuffer.
- if (heldRecordBuffer != null && heldRecordBuffer.size() > 0) {
- int heldLen = heldRecordBuffer.size();
-
- // Ensure the capacity of this buffer.
- int newCount = count + heldLen - headerOffset;
- ensureCapacity(newCount);
-
- // Slide everything in the buffer to the right.
- System.arraycopy(buf, headerOffset,
- buf, heldLen, count - headerOffset);
-
- // Prepend the held record to the buffer.
- System.arraycopy(
- heldRecordBuffer.toByteArray(), 0, buf, 0, heldLen);
- count = newCount;
- headerOffset = 0;
-
- // Clear the held buffer.
- heldRecordBuffer.reset();
-
- // The held buffer has been dumped, set the debug dump offset.
- debugOffset = heldLen;
- }
- writeBuffer(s, buf, headerOffset,
- count - headerOffset, debugOffset);
- }
-
- reset();
- }
+ int v3CSOffset = v3CSLenOffset + 2; // skip length field
+ int v2CSLen = 0;
- /*
- * Actually do the write here. For SSLEngine's HS data,
- * we'll override this method and let it take the appropriate
- * action.
- */
- void writeBuffer(OutputStream s, byte [] buf, int off, int len,
- int debugOffset) throws IOException {
- s.write(buf, off, len);
- s.flush();
-
- // Output only the record from the specified debug offset.
- if (debug != null && Debug.isOn("packet")) {
- try {
- HexDumpEncoder hd = new HexDumpEncoder();
-
- System.out.println("[Raw write]: length = " +
- (len - debugOffset));
- hd.encodeBuffer(new ByteArrayInputStream(buf,
- off + debugOffset, len - debugOffset), System.out);
- } catch (IOException e) { }
- }
- }
-
- /*
- * Return whether the buffer contains a ClientHello message that should
- * be converted to V2 format.
- */
- private boolean useV2Hello() {
- return firstMessage
- && (helloVersion == ProtocolVersion.SSL20Hello)
- && (contentType == ct_handshake)
- && (buf[headerOffset + 5] == HandshakeMessage.ht_client_hello)
- // 5: recode header size
- && (buf[headerPlusMaxIVSize + 4 + 2 + 32] == 0);
- // V3 session ID is empty
- // 4: handshake header size
- // 2: client_version in ClientHello
- // 32: random in ClientHello
- }
-
- /*
- * Detect "old" servers which are capable of SSL V2.0 protocol ... for
- * example, Netscape Commerce 1.0 servers. The V3 message is in the
- * header and the bytes passed as parameter. This routine translates
- * the V3 message into an equivalent V2 one.
- *
- * Note that the translation will strip off all hello extensions as
- * SSL V2.0 does not support hello extension.
- */
- private void V3toV2ClientHello(byte v3Msg []) throws SSLException {
- int v3SessionIdLenOffset = 2 + 32; // version + nonce
- int v3SessionIdLen = v3Msg[v3SessionIdLenOffset];
- int v3CipherSpecLenOffset = v3SessionIdLenOffset + 1 + v3SessionIdLen;
- int v3CipherSpecLen = ((v3Msg[v3CipherSpecLenOffset] & 0xff) << 8) +
- (v3Msg[v3CipherSpecLenOffset + 1] & 0xff);
- int cipherSpecs = v3CipherSpecLen / 2; // 2 bytes each in V3
-
- /*
- * Copy over the cipher specs. We don't care about actually translating
- * them for use with an actual V2 server since we only talk V3.
- * Therefore, just copy over the V3 cipher spec values with a leading
- * 0.
- */
- int v3CipherSpecOffset = v3CipherSpecLenOffset + 2; // skip length
- int v2CipherSpecLen = 0;
- count = 11;
+ dstBuf.position(11);
boolean containsRenegoInfoSCSV = false;
for (int i = 0; i < cipherSpecs; i++) {
byte byte1, byte2;
- byte1 = v3Msg[v3CipherSpecOffset++];
- byte2 = v3Msg[v3CipherSpecOffset++];
- v2CipherSpecLen += V3toV2CipherSuite(byte1, byte2);
+ byte1 = fragment[v3CSOffset++];
+ byte2 = fragment[v3CSOffset++];
+ v2CSLen += V3toV2CipherSuite(dstBuf, byte1, byte2);
if (!containsRenegoInfoSCSV &&
- byte1 == (byte)0x00 && byte2 == (byte)0xFF) {
+ byte1 == (byte)0x00 && byte2 == (byte)0xFF) {
containsRenegoInfoSCSV = true;
}
}
if (!containsRenegoInfoSCSV) {
- v2CipherSpecLen += V3toV2CipherSuite((byte)0x00, (byte)0xFF);
+ v2CSLen += V3toV2CipherSuite(dstBuf, (byte)0x00, (byte)0xFF);
}
/*
+ * Copy in the nonce.
+ */
+ dstBuf.put(fragment, (offset + 2), 32);
+
+ /*
* Build the first part of the V3 record header from the V2 one
* that's now buffered up. (Lengths are fixed up later).
*/
- buf[2] = HandshakeMessage.ht_client_hello;
- buf[3] = v3Msg[0]; // major version
- buf[4] = v3Msg[1]; // minor version
- buf[5] = (byte)(v2CipherSpecLen >>> 8);
- buf[6] = (byte)v2CipherSpecLen;
- buf[7] = 0;
- buf[8] = 0; // always no session
- buf[9] = 0;
- buf[10] = 32; // nonce length (always 32 in V3)
+ int msgLen = dstBuf.position() - 2; // Exclude the legth field itself
+ dstBuf.position(0);
+ dstBuf.put((byte)(0x80 | ((msgLen >>> 8) & 0xFF))); // pos: 0
+ dstBuf.put((byte)(msgLen & 0xFF)); // pos: 1
+ dstBuf.put(HandshakeMessage.ht_client_hello); // pos: 2
+ dstBuf.put(fragment[offset]); // major version, pos: 3
+ dstBuf.put(fragment[offset + 1]); // minor version, pos: 4
+ dstBuf.put((byte)(v2CSLen >>> 8)); // pos: 5
+ dstBuf.put((byte)(v2CSLen & 0xFF)); // pos: 6
+ dstBuf.put((byte)0x00); // session_id_length, pos: 7
+ dstBuf.put((byte)0x00); // pos: 8
+ dstBuf.put((byte)0x00); // challenge_length, pos: 9
+ dstBuf.put((byte)32); // pos: 10
- /*
- * Copy in the nonce.
- */
- System.arraycopy(v3Msg, 2, buf, count, 32);
- count += 32;
+ dstBuf.position(0);
+ dstBuf.limit(msgLen + 2);
- /*
- * Set the length of the message.
- */
- count -= 2; // don't include length field itself
- buf[0] = (byte)(count >>> 8);
- buf[0] |= 0x80;
- buf[1] = (byte)(count);
- count += 2;
+ return dstBuf;
}
- /*
- * Mappings from V3 cipher suite encodings to their pure V2 equivalents.
- * This is taken from the SSL V3 specification, Appendix E.
- */
- private static int[] V3toV2CipherMap1 =
- {-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
- private static int[] V3toV2CipherMap3 =
- {-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
+ private static int V3toV2CipherSuite(ByteBuffer dstBuf,
+ byte byte1, byte byte2) {
+ dstBuf.put((byte)0);
+ dstBuf.put(byte1);
+ dstBuf.put(byte2);
- /*
- * See which matching pure-V2 cipher specs we need to include.
- * We are including these not because we are actually prepared
- * to talk V2 but because the Oracle Web Server insists on receiving
- * at least 1 "pure V2" cipher suite that it supports and returns an
- * illegal_parameter alert unless one is present. Rather than mindlessly
- * claiming to implement all documented pure V2 cipher suites the code below
- * just claims to implement the V2 cipher suite that is "equivalent"
- * in terms of cipher algorithm & exportability with the actual V3 cipher
- * suite that we do support.
- */
- private int V3toV2CipherSuite(byte byte1, byte byte2) {
- buf[count++] = 0;
- buf[count++] = byte1;
- buf[count++] = byte2;
-
- if (((byte2 & 0xff) > 0xA) ||
- (V3toV2CipherMap1[byte2] == -1)) {
+ if (((byte2 & 0xff) > 0xA) || (V3toV2CipherMap1[byte2] == -1)) {
return 3;
}
- buf[count++] = (byte)V3toV2CipherMap1[byte2];
- buf[count++] = 0;
- buf[count++] = (byte)V3toV2CipherMap3[byte2];
+ dstBuf.put((byte)V3toV2CipherMap1[byte2]);
+ dstBuf.put((byte)0);
+ dstBuf.put((byte)V3toV2CipherMap3[byte2]);
return 6;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Plaintext.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+
+/*
+ * Plaintext
+ */
+final class Plaintext {
+ final static Plaintext PLAINTEXT_NULL = new Plaintext();
+
+ byte contentType;
+ byte majorVersion;
+ byte minorVersion;
+ int recordEpoch; // incremented on every cipher state change
+ long recordSN;
+ ByteBuffer fragment; // null if need to be reassembled
+
+ HandshakeStatus handshakeStatus; // null if not used or not handshaking
+
+ Plaintext() {
+ this.contentType = 0;
+ this.majorVersion = 0;
+ this.minorVersion = 0;
+ this.recordEpoch = -1;
+ this.recordSN = -1;
+ this.fragment = null;
+ this.handshakeStatus = null;
+ }
+
+ Plaintext(byte contentType, byte majorVersion, byte minorVersion,
+ int recordEpoch, long recordSN, ByteBuffer fragment) {
+
+ this.contentType = contentType;
+ this.majorVersion = majorVersion;
+ this.minorVersion = minorVersion;
+ this.recordEpoch = recordEpoch;
+ this.recordSN = recordSN;
+ this.fragment = fragment;
+
+ this.handshakeStatus = null;
+ }
+
+ public String toString() {
+ return "contentType: " + contentType + "/" +
+ "majorVersion: " + majorVersion + "/" +
+ "minorVersion: " + minorVersion + "/" +
+ "recordEpoch: " + recordEpoch + "/" +
+ "recordSN: 0x" + Long.toHexString(recordSN) + "/" +
+ "fragment: " + fragment;
+ }
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ProtocolList.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ProtocolList.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -124,8 +124,9 @@
ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) {
ProtocolVersion selectedVersion = null;
for (ProtocolVersion pv : protocols) {
- if (pv.v > protocolVersion.v) {
- break; // Safe to break here as this.protocols is sorted
+ if (pv.compareTo(protocolVersion) > 0) {
+ break; // Safe to break here as this.protocols is sorted,
+ // and DTLS and SSL/TLS protocols are not mixed.
}
selectedVersion = pv;
}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,7 @@
import java.util.*;
import java.security.CryptoPrimitive;
+import sun.security.ssl.CipherSuite.*;
/**
* Type safe enum for an SSL/TLS protocol version. Instances are obtained
@@ -61,9 +62,9 @@
// Dummy protocol version value for invalid SSLSession
final static ProtocolVersion NONE = new ProtocolVersion(-1, "NONE");
- // If enabled, send/ accept SSLv2 hello messages
- final static ProtocolVersion SSL20Hello = new ProtocolVersion(0x0002,
- "SSLv2Hello");
+ // If enabled, send/accept SSLv2 hello messages
+ final static ProtocolVersion SSL20Hello =
+ new ProtocolVersion(0x0002, "SSLv2Hello");
// SSL 3.0
final static ProtocolVersion SSL30 = new ProtocolVersion(0x0300, "SSLv3");
@@ -77,6 +78,19 @@
// TLS 1.2
final static ProtocolVersion TLS12 = new ProtocolVersion(0x0303, "TLSv1.2");
+ // DTLS 1.0
+ // {254, 255}, the version value of DTLS 1.0.
+ final static ProtocolVersion DTLS10 =
+ new ProtocolVersion(0xFEFF, "DTLSv1.0");
+
+ // No DTLS 1.1, that version number was skipped in order to harmonize
+ // version numbers with TLS.
+
+ // DTLS 1.2
+ // {254, 253}, the version value of DTLS 1.2.
+ final static ProtocolVersion DTLS12 =
+ new ProtocolVersion(0xFEFD, "DTLSv1.2");
+
private static final boolean FIPS = SunJSSE.isFIPS();
// minimum version we implement (SSL 3.0)
@@ -85,8 +99,11 @@
// maximum version we implement (TLS 1.2)
final static ProtocolVersion MAX = TLS12;
- // ProtocolVersion to use by default (TLS 1.2)
- final static ProtocolVersion DEFAULT = TLS12;
+ // SSL/TLS ProtocolVersion to use by default (TLS 1.2)
+ final static ProtocolVersion DEFAULT_TLS = TLS12;
+
+ // DTLS ProtocolVersion to use by default (TLS 1.2)
+ final static ProtocolVersion DEFAULT_DTLS = DTLS12;
// Default version for hello messages (SSLv2Hello)
final static ProtocolVersion DEFAULT_HELLO = FIPS ? TLS10 : SSL30;
@@ -108,10 +125,10 @@
// Initialize the available protocols.
static {
- Set<ProtocolVersion> protocols = new HashSet<>(5);
+ Set<ProtocolVersion> protocols = new HashSet<>(7);
ProtocolVersion[] pvs = new ProtocolVersion[] {
- SSL20Hello, SSL30, TLS10, TLS11, TLS12};
+ SSL20Hello, SSL30, TLS10, TLS11, TLS12, DTLS10, DTLS12};
EnumSet<CryptoPrimitive> cryptoPrimitives =
EnumSet.<CryptoPrimitive>of(CryptoPrimitive.KEY_AGREEMENT);
for (ProtocolVersion p : pvs) {
@@ -145,6 +162,10 @@
return TLS12;
} else if (v == SSL20Hello.v) {
return SSL20Hello;
+ } else if (v == DTLS10.v) {
+ return DTLS10;
+ } else if (v == DTLS12.v) {
+ return DTLS12;
} else {
int major = (v >>> 8) & 0xFF;
int minor = v & 0xFF;
@@ -172,8 +193,8 @@
}
if (FIPS && (name.equals(SSL30.name) || name.equals(SSL20Hello.name))) {
- throw new IllegalArgumentException
- ("Only TLS 1.0 or later allowed in FIPS mode");
+ throw new IllegalArgumentException(
+ "Only TLS 1.0 or later allowed in FIPS mode");
}
if (name.equals(SSL30.name)) {
@@ -186,6 +207,10 @@
return TLS12;
} else if (name.equals(SSL20Hello.name)) {
return SSL20Hello;
+ } else if (name.equals(DTLS10.name)) {
+ return DTLS10;
+ } else if (name.equals(DTLS12.name)) {
+ return DTLS12;
} else {
throw new IllegalArgumentException(name);
}
@@ -201,6 +226,90 @@
*/
@Override
public int compareTo(ProtocolVersion protocolVersion) {
- return this.v - protocolVersion.v;
+ if (maybeDTLSProtocol()) {
+ if (!protocolVersion.maybeDTLSProtocol()) {
+ throw new IllegalArgumentException("Not DTLS protocol");
+ }
+
+ return protocolVersion.v - this.v;
+ } else {
+ if (protocolVersion.maybeDTLSProtocol()) {
+ throw new IllegalArgumentException("Not TLS protocol");
+ }
+
+ return this.v - protocolVersion.v;
+ }
+ }
+
+ /**
+ * Returns true if a ProtocolVersion represents a DTLS protocol.
+ */
+ boolean isDTLSProtocol() {
+ return this.v == DTLS12.v || this.v == DTLS10.v;
+ }
+
+ /**
+ * Returns true if a ProtocolVersion may represent a DTLS protocol.
+ */
+ boolean maybeDTLSProtocol() {
+ return (this.major & 0x80) != 0;
+ }
+
+ boolean useTLS12PlusSpec() {
+ return maybeDTLSProtocol() ? (this.v <= DTLS12.v) : (this.v >= TLS12.v);
+ }
+
+ boolean useTLS11PlusSpec() {
+ return maybeDTLSProtocol() ? true : (this.v >= TLS11.v);
+ }
+
+ boolean useTLS10PlusSpec() {
+ return maybeDTLSProtocol() ? true : (this.v >= TLS10.v);
+ }
+
+ boolean obsoletes(CipherSuite suite) {
+ ProtocolVersion proto = this;
+ if (proto.isDTLSProtocol()) {
+ // DTLS bans stream ciphers.
+ if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
+ return true;
+ }
+
+ proto = mapToTLSProtocol(this);
+ }
+
+ return (proto.v >= suite.obsoleted);
+ }
+
+ boolean supports(CipherSuite suite) {
+ ProtocolVersion proto = this;
+ if (proto.isDTLSProtocol()) {
+ // DTLS bans stream ciphers.
+ if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
+ return false;
+ }
+
+ proto = mapToTLSProtocol(this);
+ }
+
+ return (proto.v >= suite.supported);
+ }
+
+ // Map a specified protocol to the corresponding TLS version.
+ //
+ // DTLS 1.2 -> TLS 1.2
+ // DTLS 1.0 -> TLS 1.1
+ private static ProtocolVersion mapToTLSProtocol(
+ ProtocolVersion protocolVersion) {
+
+ if (protocolVersion.isDTLSProtocol()) {
+ if (protocolVersion.v == DTLS10.v) {
+ protocolVersion = TLS11;
+ } else { // DTLS12
+ protocolVersion = TLS12;
+ }
+ }
+
+ return protocolVersion;
}
}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java Thu Jun 04 18:49:37 2015 -0700
@@ -73,8 +73,8 @@
this.protocolVersion = protocolVersion;
try {
- String s = ((protocolVersion.v >= ProtocolVersion.TLS12.v) ?
- "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret");
+ String s = protocolVersion.useTLS12PlusSpec() ?
+ "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret";
KeyGenerator kg = JsseJce.getKeyGenerator(s);
kg.init(new TlsRsaPremasterSecretParameterSpec(
maxVersion.v, protocolVersion.v), generator);
@@ -103,7 +103,7 @@
throw new SSLKeyException("Private key not of type RSA");
}
- if (currentVersion.v >= ProtocolVersion.TLS10.v) {
+ if (currentVersion.useTLS10PlusSpec()) {
encrypted = input.getBytes16();
} else {
encrypted = new byte [messageSize];
@@ -142,7 +142,7 @@
@Override
int messageLength() {
- if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+ if (protocolVersion.useTLS10PlusSpec()) {
return encrypted.length + 2;
} else {
return encrypted.length;
@@ -151,7 +151,7 @@
@Override
void send(HandshakeOutStream s) throws IOException {
- if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+ if (protocolVersion.useTLS10PlusSpec()) {
s.putBytes16(encrypted);
} else {
s.write(encrypted);
--- a/jdk/src/java.base/share/classes/sun/security/ssl/RandomCookie.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/RandomCookie.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -70,10 +70,10 @@
void print(PrintStream s) {
int i, gmt_unix_time;
- gmt_unix_time = random_bytes[0] << 24;
- gmt_unix_time += random_bytes[1] << 16;
- gmt_unix_time += random_bytes[2] << 8;
- gmt_unix_time += random_bytes[3];
+ gmt_unix_time = ((random_bytes[0] & 0xFF) << 24) |
+ ((random_bytes[1] & 0xFF) << 16) |
+ ((random_bytes[2] & 0xFF) << 8) |
+ (random_bytes[3] & 0xFF);
s.print("GMT: " + gmt_unix_time + " ");
s.print("bytes = { ");
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Record.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Record.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,21 +23,20 @@
* questions.
*/
-
package sun.security.ssl;
-
/**
- * SSL/TLS records, as pulled off (and put onto) a TCP stream. This is
+ * SSL/TLS/DTLS records, as pulled off (and put onto) a TCP stream. This is
* the base interface, which defines common information and interfaces
* used by both Input and Output records.
*
* @author David Brownell
*/
interface Record {
+
/*
- * There are four SSL record types, which are part of the interface
- * to this level (along with the maximum record size)
+ * There are four record types, which are part of the interface
+ * to this level (along with the maximum record size).
*
* enum { change_cipher_spec(20), alert(21), handshake(22),
* application_data(23), (255) } ContentType;
@@ -47,75 +46,20 @@
static final byte ct_handshake = 22;
static final byte ct_application_data = 23;
- static final int headerSize = 5; // SSLv3 record header
- static final int maxExpansion = 1024; // for bad compression
- static final int trailerSize = 20; // SHA1 hash size
+ static final int maxMacSize = 48; // the max supported MAC or
+ // AEAD tag size
static final int maxDataSize = 16384; // 2^14 bytes of data
static final int maxPadding = 256; // block cipher padding
- static final int maxIVLength = 256; // IV length
-
- /*
- * The size of the header plus the max IV length
- */
- static final int headerPlusMaxIVSize =
- headerSize // header
- + maxIVLength; // iv
+ static final int maxIVLength = 16; // the max supported IV length
- /*
- * SSL has a maximum record size. It's header, (compressed) data,
- * padding, and a trailer for the message authentication information (MAC
- * for block and stream ciphers, and message authentication tag for AEAD
- * ciphers).
- *
- * Some compression algorithms have rare cases where they expand the data.
- * As we don't support compression at this time, leave that out.
- */
- static final int maxRecordSize =
- headerPlusMaxIVSize // header + iv
- + maxDataSize // data
- + maxPadding // padding
- + trailerSize; // MAC or AEAD tag
-
- static final boolean enableCBCProtection =
- Debug.getBooleanProperty("jsse.enableCBCProtection", true);
+ static final int maxFragmentSize = 18432; // the max fragment size
+ // 2^14 + 2048
/*
- * For CBC protection in SSL3/TLS1, we break some plaintext into two
- * packets. Max application data size for the second packet.
+ * System property to enable/disable CBC protection in SSL3/TLS1.
*/
- static final int maxDataSizeMinusOneByteRecord =
- maxDataSize // max data size
- - ( // max one byte record size
- headerPlusMaxIVSize // header + iv
- + 1 // one byte data
- + maxPadding // padding
- + trailerSize // MAC
- );
-
- /*
- * The maximum large record size.
- *
- * Some SSL/TLS implementations support large fragment upto 2^15 bytes,
- * such as Microsoft. We support large incoming fragments.
- *
- * The maximum large record size is defined as maxRecordSize plus 2^14,
- * this is the amount OpenSSL is using.
- */
- static final int maxLargeRecordSize =
- maxRecordSize // Max size with a conforming implementation
- + maxDataSize; // extra 2^14 bytes for large data packets.
-
-
- /*
- * Maximum record size for alert and change cipher spec records.
- * They only contain 2 and 1 bytes of data, respectively.
- * Allocate a smaller array.
- */
- static final int maxAlertRecordSize =
- headerPlusMaxIVSize // header + iv
- + 2 // alert
- + maxPadding // padding
- + trailerSize; // MAC
+ static final boolean enableCBCProtection =
+ Debug.getBooleanProperty("jsse.enableCBCProtection", true);
/*
* The overflow values of integers of 8, 16 and 24 bits.
@@ -123,4 +67,27 @@
static final int OVERFLOW_OF_INT08 = (1 << 8);
static final int OVERFLOW_OF_INT16 = (1 << 16);
static final int OVERFLOW_OF_INT24 = (1 << 24);
+
+ /**
+ * Return a description for the given content type.
+ */
+ static String contentName(byte contentType) {
+ switch (contentType) {
+ case ct_change_cipher_spec:
+ return "Change Cipher Spec";
+ case ct_alert:
+ return "Alert";
+ case ct_handshake:
+ return "Handshake";
+ case ct_application_data:
+ return "Application Data";
+ default:
+ return "contentType = " + contentType;
+ }
+ }
+
+ static boolean isValidContentType(byte contentType) {
+ return (contentType == 20) || (contentType == 21) ||
+ (contentType == 22) || (contentType == 23);
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/RecordType.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/*
+ * enumation of record type
+ */
+enum RecordType {
+
+ RECORD_CHANGE_CIPHER_SPEC (Record.ct_change_cipher_spec,
+ HandshakeMessage.ht_not_applicable),
+ RECORD_ALERT (Record.ct_alert,
+ HandshakeMessage.ht_not_applicable),
+ RECORD_HELLO_REQUEST (Record.ct_handshake,
+ HandshakeMessage.ht_hello_request),
+ RECORD_CLIENT_HELLO (Record.ct_handshake,
+ HandshakeMessage.ht_client_hello),
+ RECORD_SERVER_HELLO (Record.ct_handshake,
+ HandshakeMessage.ht_server_hello),
+ RECORD_HELLO_VERIFY_REQUEST (Record.ct_handshake,
+ HandshakeMessage.ht_hello_verify_request),
+ RECORD_NEW_SESSION_TICKET (Record.ct_handshake,
+ HandshakeMessage.ht_new_session_ticket),
+ RECORD_CERTIFICATE (Record.ct_handshake,
+ HandshakeMessage.ht_certificate),
+ RECORD_SERVER_KEY_EXCHANGE (Record.ct_handshake,
+ HandshakeMessage.ht_server_key_exchange),
+ RECORD_CERTIFICATE_REQUEST (Record.ct_handshake,
+ HandshakeMessage.ht_certificate_request),
+ RECORD_SERVER_HELLO_DONE (Record.ct_handshake,
+ HandshakeMessage.ht_server_hello_done),
+ RECORD_CERTIFICATE_VERIFY (Record.ct_handshake,
+ HandshakeMessage.ht_certificate_verify),
+ RECORD_CLIENT_KEY_EXCHANGE (Record.ct_handshake,
+ HandshakeMessage.ht_client_key_exchange),
+ RECORD_FINISHED (Record.ct_handshake,
+ HandshakeMessage.ht_finished),
+ RECORD_CERTIFICATE_URL (Record.ct_handshake,
+ HandshakeMessage.ht_certificate_url),
+ RECORD_CERTIFICATE_STATUS (Record.ct_handshake,
+ HandshakeMessage.ht_certificate_status),
+ RECORD_SUPPLIEMENTAL_DATA (Record.ct_handshake,
+ HandshakeMessage.ht_supplemental_data),
+ RECORD_APPLICATION_DATA (Record.ct_application_data,
+ HandshakeMessage.ht_not_applicable);
+
+ byte contentType;
+ byte handshakeType;
+
+ private RecordType(byte contentType, byte handshakeType) {
+ this.contentType = contentType;
+ this.handshakeType = handshakeType;
+ }
+
+ static RecordType valueOf(byte contentType, byte handshakeType) {
+ if (contentType == Record.ct_change_cipher_spec) {
+ return RECORD_CHANGE_CIPHER_SPEC;
+ } else if (contentType == Record.ct_alert) {
+ return RECORD_ALERT;
+ } else if (contentType == Record.ct_application_data) {
+ return RECORD_APPLICATION_DATA;
+ } else if (handshakeType == HandshakeMessage.ht_hello_request) {
+ return RECORD_HELLO_REQUEST;
+ } else if (handshakeType == HandshakeMessage.ht_client_hello) {
+ return RECORD_CLIENT_HELLO;
+ } else if (handshakeType == HandshakeMessage.ht_server_hello) {
+ return RECORD_SERVER_HELLO;
+ } else if (handshakeType == HandshakeMessage.ht_hello_verify_request) {
+ return RECORD_HELLO_VERIFY_REQUEST;
+ } else if (handshakeType == HandshakeMessage.ht_new_session_ticket) {
+ return RECORD_NEW_SESSION_TICKET;
+ } else if (handshakeType == HandshakeMessage.ht_certificate) {
+ return RECORD_CERTIFICATE;
+ } else if (handshakeType == HandshakeMessage.ht_server_key_exchange) {
+ return RECORD_SERVER_KEY_EXCHANGE;
+ } else if (handshakeType == HandshakeMessage.ht_certificate_request) {
+ return RECORD_CERTIFICATE_REQUEST;
+ } else if (handshakeType == HandshakeMessage.ht_server_hello_done) {
+ return RECORD_SERVER_HELLO_DONE;
+ } else if (handshakeType == HandshakeMessage.ht_certificate_verify) {
+ return RECORD_CERTIFICATE_VERIFY;
+ } else if (handshakeType == HandshakeMessage.ht_client_key_exchange) {
+ return RECORD_CLIENT_KEY_EXCHANGE;
+ } else if (handshakeType == HandshakeMessage.ht_finished) {
+ return RECORD_FINISHED;
+ } else if (handshakeType == HandshakeMessage.ht_certificate_url) {
+ return RECORD_CERTIFICATE_URL;
+ } else if (handshakeType == HandshakeMessage.ht_certificate_status) {
+ return RECORD_CERTIFICATE_STATUS;
+ } else if (handshakeType == HandshakeMessage.ht_supplemental_data) {
+ return RECORD_SUPPLIEMENTAL_DATA;
+ }
+
+ // otherwise, invalid record type
+ throw new IllegalArgumentException(
+ "Invalid record type (ContentType:" + contentType +
+ ", HandshakeType:" + handshakeType + ")");
+ }
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java Thu Jun 04 18:49:37 2015 -0700
@@ -352,18 +352,13 @@
components.add("ECDH_ANON");
}
break;
- case K_KRB5:
- if (!forCertPathOnly) {
- components.add("KRB5");
+ default:
+ if (ClientKeyExchangeService.find(keyExchange.name) != null) {
+ if (!forCertPathOnly) {
+ components.add(keyExchange.name);
+ }
}
- break;
- case K_KRB5_EXPORT:
- if (!forCertPathOnly) {
- components.add("KRB5_EXPORT");
- }
- break;
- default:
- // ignore
+ // otherwise ignore
}
return components;
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Thu Jun 04 18:49:37 2015 -0700
@@ -62,6 +62,9 @@
private CipherSuiteList defaultClientCipherSuiteList;
private CipherSuiteList supportedCipherSuiteList;
+ // DTLS cookie exchange manager
+ private HelloCookieManager helloCookieManager;
+
SSLContextImpl() {
ephemeralKeyManager = new EphemeralKeyManager();
clientCache = new SSLSessionContextImpl();
@@ -175,11 +178,29 @@
return DummyX509KeyManager.INSTANCE;
}
+ abstract SSLEngine createSSLEngineImpl();
+ abstract SSLEngine createSSLEngineImpl(String host, int port);
+
+ @Override
+ protected SSLEngine engineCreateSSLEngine() {
+ if (!isInitialized) {
+ throw new IllegalStateException("SSLContext is not initialized");
+ }
+ return createSSLEngineImpl();
+ }
+
+ @Override
+ protected SSLEngine engineCreateSSLEngine(String host, int port) {
+ if (!isInitialized) {
+ throw new IllegalStateException("SSLContext is not initialized");
+ }
+ return createSSLEngineImpl(host, port);
+ }
+
@Override
protected SSLSocketFactory engineGetSocketFactory() {
if (!isInitialized) {
- throw new IllegalStateException(
- "SSLContextImpl is not initialized");
+ throw new IllegalStateException("SSLContext is not initialized");
}
return new SSLSocketFactoryImpl(this);
}
@@ -193,24 +214,6 @@
}
@Override
- protected SSLEngine engineCreateSSLEngine() {
- if (!isInitialized) {
- throw new IllegalStateException(
- "SSLContextImpl is not initialized");
- }
- return new SSLEngineImpl(this);
- }
-
- @Override
- protected SSLEngine engineCreateSSLEngine(String host, int port) {
- if (!isInitialized) {
- throw new IllegalStateException(
- "SSLContextImpl is not initialized");
- }
- return new SSLEngineImpl(this, host, port);
- }
-
- @Override
protected SSLSessionContext engineGetClientSessionContext() {
return clientCache;
}
@@ -236,6 +239,23 @@
return ephemeralKeyManager;
}
+ HelloCookieManager getHelloCookieManager() {
+ if (!isInitialized) {
+ throw new IllegalStateException("SSLContext is not initialized");
+ }
+
+ if (helloCookieManager == null) {
+ helloCookieManager = getHelloCookieManager(secureRandom);
+ }
+
+ return helloCookieManager;
+ }
+
+ HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) {
+ throw new UnsupportedOperationException(
+ "Cookie exchange applies to DTLS only");
+ }
+
abstract SSLParameters getDefaultServerSSLParams();
abstract SSLParameters getDefaultClientSSLParams();
abstract SSLParameters getSupportedSSLParams();
@@ -319,12 +339,20 @@
(protocols == defaultClientProtocolList);
}
+ /**
+ * Return whether a protocol list is the original default enabled
+ * protocols. See: SSLSocket/SSLEngine.setEnabledProtocols()
+ */
+ boolean isDefaultCipherSuiteList(CipherSuiteList cipherSuites) {
+ return (cipherSuites == defaultClientCipherSuiteList) ||
+ (cipherSuites == defaultServerCipherSuiteList);
+ }
/*
* Return the list of all available CipherSuites with a priority of
* minPriority or above.
*/
- private CipherSuiteList getApplicableCipherSuiteList(
+ private static CipherSuiteList getApplicableCipherSuiteList(
ProtocolList protocols, boolean onlyEnabled) {
int minPriority = CipherSuite.SUPPORTED_SUITES_PRIORITY;
@@ -344,8 +372,8 @@
}
if (suite.isAvailable() &&
- suite.obsoleted > protocols.min.v &&
- suite.supported <= protocols.max.v) {
+ !protocols.min.obsoletes(suite) &&
+ protocols.max.supports(suite)) {
if (SSLAlgorithmConstraints.DEFAULT.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
suite.name, null)) {
@@ -353,10 +381,10 @@
}
} else if (debug != null &&
Debug.isOn("sslctx") && Debug.isOn("verbose")) {
- if (suite.obsoleted <= protocols.min.v) {
+ if (protocols.min.obsoletes(suite)) {
System.out.println(
"Ignoring obsoleted cipher suite: " + suite);
- } else if (suite.supported > protocols.max.v) {
+ } else if (!protocols.max.supports(suite)) {
System.out.println(
"Ignoring unsupported cipher suite: " + suite);
} else {
@@ -388,8 +416,25 @@
}
}
+ private static String[] getAvailableProtocols(
+ ProtocolVersion[] protocolCandidates) {
+
+ List<String> availableProtocols = Collections.<String>emptyList();
+ if (protocolCandidates != null && protocolCandidates.length != 0) {
+ availableProtocols = new ArrayList<>(protocolCandidates.length);
+ for (ProtocolVersion p : protocolCandidates) {
+ if (ProtocolVersion.availableProtocols.contains(p)) {
+ availableProtocols.add(p.name);
+ }
+ }
+ }
+
+ return availableProtocols.toArray(new String[0]);
+ }
+
+
/*
- * The SSLContext implementation for TLS/SSL algorithm
+ * The SSLContext implementation for SSL/(D)TLS algorithm
*
* SSL/TLS protocols specify the forward compatibility and version
* roll-back attack protections, however, a number of SSL/TLS server
@@ -418,14 +463,15 @@
*/
/*
- * The base abstract SSLContext implementation.
+ * The base abstract SSLContext implementation for the Transport Layer
+ * Security (TLS) protocols.
*
* This abstract class encapsulates supported and the default server
- * SSL parameters.
+ * SSL/TLS parameters.
*
* @see SSLContext
*/
- private abstract static class AbstractSSLContext extends SSLContextImpl {
+ private abstract static class AbstractTLSContext extends SSLContextImpl {
// parameters
private static final SSLParameters defaultServerSSLParams;
private static final SSLParameters supportedSSLParams;
@@ -482,20 +528,14 @@
return supportedSSLParams;
}
- static String[] getAvailableProtocols(
- ProtocolVersion[] protocolCandidates) {
+ @Override
+ SSLEngine createSSLEngineImpl() {
+ return new SSLEngineImpl(this, false);
+ }
- List<String> availableProtocols = Collections.<String>emptyList();
- if (protocolCandidates != null && protocolCandidates.length != 0) {
- availableProtocols = new ArrayList<>(protocolCandidates.length);
- for (ProtocolVersion p : protocolCandidates) {
- if (ProtocolVersion.availableProtocols.contains(p)) {
- availableProtocols.add(p.name);
- }
- }
- }
-
- return availableProtocols.toArray(new String[0]);
+ @Override
+ SSLEngine createSSLEngineImpl(String host, int port) {
+ return new SSLEngineImpl(this, host, port, false);
}
}
@@ -504,7 +544,7 @@
*
* @see SSLContext
*/
- public static final class TLS10Context extends AbstractSSLContext {
+ public static final class TLS10Context extends AbstractTLSContext {
private static final SSLParameters defaultClientSSLParams;
static {
@@ -537,7 +577,7 @@
*
* @see SSLContext
*/
- public static final class TLS11Context extends AbstractSSLContext {
+ public static final class TLS11Context extends AbstractTLSContext {
private static final SSLParameters defaultClientSSLParams;
static {
@@ -572,7 +612,7 @@
*
* @see SSLContext
*/
- public static final class TLS12Context extends AbstractSSLContext {
+ public static final class TLS12Context extends AbstractTLSContext {
private static final SSLParameters defaultClientSSLParams;
static {
@@ -605,12 +645,73 @@
}
/*
+ * The interface for the customized SSL/(D)TLS SSLContext.
+ *
+ * @see SSLContext
+ */
+ private static class CustomizedSSLProtocols {
+ private final static String PROPERTY_NAME = "jdk.tls.client.protocols";
+ static IllegalArgumentException reservedException = null;
+ static ArrayList<ProtocolVersion>
+ customizedProtocols = new ArrayList<>();
+
+ // Don't want a java.lang.LinkageError for illegal system property.
+ //
+ // Please don't throw exception in this static block. Otherwise,
+ // java.lang.LinkageError may be thrown during the instantiation of
+ // the provider service. Instead, please handle the initialization
+ // exception in the caller's constructor.
+ static {
+ String property = AccessController.doPrivileged(
+ new GetPropertyAction(PROPERTY_NAME));
+ if (property != null && property.length() != 0) {
+ // remove double quote marks from beginning/end of the property
+ if (property.length() > 1 && property.charAt(0) == '"' &&
+ property.charAt(property.length() - 1) == '"') {
+ property = property.substring(1, property.length() - 1);
+ }
+ }
+
+ if (property != null && property.length() != 0) {
+ String[] protocols = property.split(",");
+ for (int i = 0; i < protocols.length; i++) {
+ protocols[i] = protocols[i].trim();
+ // Is it a supported protocol name?
+ try {
+ ProtocolVersion pro =
+ ProtocolVersion.valueOf(protocols[i]);
+
+ if (SunJSSE.isFIPS() &&
+ ((pro.v == ProtocolVersion.SSL30.v) ||
+ (pro.v == ProtocolVersion.SSL20Hello.v))) {
+ reservedException = new IllegalArgumentException(
+ PROPERTY_NAME + ": " + pro +
+ " is not FIPS compliant");
+
+ break;
+ }
+
+ // ignore duplicated protocols
+ if (!customizedProtocols.contains(pro)) {
+ customizedProtocols.add(pro);
+ }
+ } catch (IllegalArgumentException iae) {
+ reservedException = new IllegalArgumentException(
+ PROPERTY_NAME + ": " + protocols[i] +
+ " is not a standard SSL protocol name", iae);
+ }
+ }
+ }
+ }
+ }
+
+ /*
* The SSLContext implementation for customized TLS protocols
*
* @see SSLContext
*/
- private static class CustomizedSSLContext extends AbstractSSLContext {
- private static final String PROPERTY_NAME = "jdk.tls.client.protocols";
+ private static class CustomizedTLSContext extends AbstractTLSContext {
+
private static final SSLParameters defaultClientSSLParams;
private static IllegalArgumentException reservedException = null;
@@ -621,78 +722,52 @@
// the provider service. Instead, let's handle the initialization
// exception in constructor.
static {
- // candidates for available protocols
- ProtocolVersion[] candidates;
-
- String property = AccessController.doPrivileged(
- new GetPropertyAction(PROPERTY_NAME));
- if (property == null || property.length() == 0) {
- // the default enabled client TLS protocols
- if (SunJSSE.isFIPS()) {
- candidates = new ProtocolVersion[] {
- ProtocolVersion.TLS10,
- ProtocolVersion.TLS11,
- ProtocolVersion.TLS12
- };
- } else {
- candidates = new ProtocolVersion[] {
- ProtocolVersion.SSL30,
- ProtocolVersion.TLS10,
- ProtocolVersion.TLS11,
- ProtocolVersion.TLS12
- };
- }
- } else {
- // remove double quote marks from beginning/end of the property
- if (property.length() > 1 && property.charAt(0) == '"' &&
- property.charAt(property.length() - 1) == '"') {
- property = property.substring(1, property.length() - 1);
- }
-
- String[] protocols = null;
- if (property != null && property.length() != 0) {
- protocols = property.split(",");
- } else {
- reservedException = new IllegalArgumentException(
- "No protocol specified in " +
- PROPERTY_NAME + " system property");
- protocols = new String[0];
- }
-
- candidates = new ProtocolVersion[protocols.length];
- for (int i = 0; i < protocols.length; i++) {
- protocols[i] = protocols[i].trim();
- // Is it a supported protocol name?
- try {
- candidates[i] = ProtocolVersion.valueOf(protocols[i]);
- } catch (IllegalArgumentException iae) {
- reservedException = new IllegalArgumentException(
- PROPERTY_NAME + ": " + protocols[i] +
- " is not a standard SSL/TLS protocol name", iae);
- break;
+ reservedException = CustomizedSSLProtocols.reservedException;
+ if (reservedException == null) {
+ ArrayList<ProtocolVersion>
+ customizedTLSProtocols = new ArrayList<>();
+ for (ProtocolVersion protocol :
+ CustomizedSSLProtocols.customizedProtocols) {
+ if (!protocol.isDTLSProtocol()) {
+ customizedTLSProtocols.add(protocol);
}
}
- if ((reservedException == null) && SunJSSE.isFIPS()) {
- for (ProtocolVersion protocolVersion : candidates) {
- if (ProtocolVersion.SSL20Hello.v == protocolVersion.v ||
- ProtocolVersion.SSL30.v == protocolVersion.v) {
- reservedException = new IllegalArgumentException(
- PROPERTY_NAME + ": " + protocolVersion +
- " is not FIPS compliant");
- }
+ // candidates for available protocols
+ ProtocolVersion[] candidates;
+ if (customizedTLSProtocols.isEmpty()) {
+ // Use the default enabled client protocols if no
+ // customized TLS protocols.
+ if (SunJSSE.isFIPS()) {
+ candidates = new ProtocolVersion[] {
+ ProtocolVersion.TLS10,
+ ProtocolVersion.TLS11,
+ ProtocolVersion.TLS12
+ };
+ } else {
+ candidates = new ProtocolVersion[] {
+ ProtocolVersion.SSL30,
+ ProtocolVersion.TLS10,
+ ProtocolVersion.TLS11,
+ ProtocolVersion.TLS12
+ };
}
+ } else {
+ // Use the customized TLS protocols.
+ candidates =
+ new ProtocolVersion[customizedTLSProtocols.size()];
+ candidates = customizedTLSProtocols.toArray(candidates);
}
- }
- defaultClientSSLParams = new SSLParameters();
- if (reservedException == null) {
+ defaultClientSSLParams = new SSLParameters();
defaultClientSSLParams.setProtocols(
getAvailableProtocols(candidates));
+ } else {
+ defaultClientSSLParams = null; // unlikely to be used
}
}
- protected CustomizedSSLContext() {
+ protected CustomizedTLSContext() {
if (reservedException != null) {
throw reservedException;
}
@@ -709,7 +784,7 @@
*
* @see SSLContext
*/
- public static final class TLSContext extends CustomizedSSLContext {
+ public static final class TLSContext extends CustomizedTLSContext {
// use the default constructor and methods
}
@@ -718,7 +793,7 @@
*
* @see SSLContext
*/
- public static final class DefaultSSLContext extends CustomizedSSLContext {
+ public static final class DefaultSSLContext extends CustomizedTLSContext {
private static final String NONE = "NONE";
private static final String P11KEYSTORE = "PKCS11";
@@ -879,6 +954,193 @@
}
}
+ /*
+ * The base abstract SSLContext implementation for the Datagram Transport
+ * Layer Security (DTLS) protocols.
+ *
+ * This abstract class encapsulates supported and the default server DTLS
+ * parameters.
+ *
+ * @see SSLContext
+ */
+ private abstract static class AbstractDTLSContext extends SSLContextImpl {
+ // parameters
+ private static final SSLParameters defaultServerSSLParams;
+ private static final SSLParameters supportedSSLParams;
+
+ static {
+ // supported SSL parameters
+ supportedSSLParams = new SSLParameters();
+
+ // Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode.
+ supportedSSLParams.setProtocols(new String[] {
+ ProtocolVersion.DTLS10.name,
+ ProtocolVersion.DTLS12.name
+ });
+
+ // candidates for available protocols
+ ProtocolVersion[] candidates = new ProtocolVersion[] {
+ ProtocolVersion.DTLS10,
+ ProtocolVersion.DTLS12
+ };
+
+ defaultServerSSLParams = new SSLParameters();
+ defaultServerSSLParams.setProtocols(
+ getAvailableProtocols(candidates));
+ }
+
+ @Override
+ SSLParameters getDefaultServerSSLParams() {
+ return defaultServerSSLParams;
+ }
+
+ @Override
+ SSLParameters getSupportedSSLParams() {
+ return supportedSSLParams;
+ }
+
+ @Override
+ SSLEngine createSSLEngineImpl() {
+ return new SSLEngineImpl(this, true);
+ }
+
+ @Override
+ SSLEngine createSSLEngineImpl(String host, int port) {
+ return new SSLEngineImpl(this, host, port, true);
+ }
+
+ @Override
+ HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) {
+ return new HelloCookieManager(secureRandom);
+ }
+ }
+
+ /*
+ * The SSLContext implementation for DTLSv1.0 algorithm.
+ *
+ * @see SSLContext
+ */
+ public static final class DTLS10Context extends AbstractDTLSContext {
+ private final static SSLParameters defaultClientSSLParams;
+
+ static {
+ // candidates for available protocols
+ ProtocolVersion[] candidates = new ProtocolVersion[] {
+ ProtocolVersion.DTLS10
+ };
+
+ defaultClientSSLParams = new SSLParameters();
+ defaultClientSSLParams.setProtocols(
+ getAvailableProtocols(candidates));
+ }
+
+ @Override
+ SSLParameters getDefaultClientSSLParams() {
+ return defaultClientSSLParams;
+ }
+ }
+
+ /*
+ * The SSLContext implementation for DTLSv1.2 algorithm.
+ *
+ * @see SSLContext
+ */
+ public static final class DTLS12Context extends AbstractDTLSContext {
+ private final static SSLParameters defaultClientSSLParams;
+
+ static {
+ // candidates for available protocols
+ ProtocolVersion[] candidates = new ProtocolVersion[] {
+ ProtocolVersion.DTLS10,
+ ProtocolVersion.DTLS12
+ };
+
+ defaultClientSSLParams = new SSLParameters();
+ defaultClientSSLParams.setProtocols(
+ getAvailableProtocols(candidates));
+ }
+
+ @Override
+ SSLParameters getDefaultClientSSLParams() {
+ return defaultClientSSLParams;
+ }
+ }
+
+ /*
+ * The SSLContext implementation for customized TLS protocols
+ *
+ * @see SSLContext
+ */
+ private static class CustomizedDTLSContext extends AbstractDTLSContext {
+ private final static SSLParameters defaultClientSSLParams;
+ private static IllegalArgumentException reservedException = null;
+
+ // Don't want a java.lang.LinkageError for illegal system property.
+ //
+ // Please don't throw exception in this static block. Otherwise,
+ // java.lang.LinkageError may be thrown during the instantiation of
+ // the provider service. Instead, let's handle the initialization
+ // exception in constructor.
+ static {
+ reservedException = CustomizedSSLProtocols.reservedException;
+ if (reservedException == null) {
+ ArrayList<ProtocolVersion>
+ customizedDTLSProtocols = new ArrayList<>();
+ for (ProtocolVersion protocol :
+ CustomizedSSLProtocols.customizedProtocols) {
+ if (protocol.isDTLSProtocol()) {
+ customizedDTLSProtocols.add(protocol);
+ }
+ }
+
+ // candidates for available protocols
+ ProtocolVersion[] candidates;
+ if (customizedDTLSProtocols.isEmpty()) {
+ // Use the default enabled client protocols if no
+ // customized TLS protocols.
+ //
+ // Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode.
+ candidates = new ProtocolVersion[] {
+ ProtocolVersion.DTLS10,
+ ProtocolVersion.DTLS12
+ };
+
+ } else {
+ // Use the customized TLS protocols.
+ candidates =
+ new ProtocolVersion[customizedDTLSProtocols.size()];
+ candidates = customizedDTLSProtocols.toArray(candidates);
+ }
+
+ defaultClientSSLParams = new SSLParameters();
+ defaultClientSSLParams.setProtocols(
+ getAvailableProtocols(candidates));
+ } else {
+ defaultClientSSLParams = null; // unlikely to be used
+ }
+ }
+
+ protected CustomizedDTLSContext() {
+ if (reservedException != null) {
+ throw reservedException;
+ }
+ }
+
+ @Override
+ SSLParameters getDefaultClientSSLParams() {
+ return defaultClientSSLParams;
+ }
+ }
+
+ /*
+ * The SSLContext implementation for default "DTLS" algorithm
+ *
+ * @see SSLContext
+ */
+ public static final class DTLSContext extends CustomizedDTLSContext {
+ // use the default constructor and methods
+ }
+
}
@@ -961,7 +1223,7 @@
ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol());
AlgorithmConstraints constraints = null;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
if (session instanceof ExtendedSSLSession) {
ExtendedSSLSession extSession =
(ExtendedSSLSession)session;
@@ -1003,7 +1265,7 @@
ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol());
AlgorithmConstraints constraints = null;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
if (session instanceof ExtendedSSLSession) {
ExtendedSSLSession extSession =
(ExtendedSSLSession)session;
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java Thu Jun 04 18:49:37 2015 -0700
@@ -54,57 +54,6 @@
* before thread1, and sends the data. The receiving side would see an
* out-of-order error.
*
- * Handshaking is still done the same way as SSLSocket using the normal
- * InputStream/OutputStream abstactions. We create
- * ClientHandshakers/ServerHandshakers, which produce/consume the
- * handshaking data. The transfer of the data is largely handled by the
- * HandshakeInStream/HandshakeOutStreams. Lastly, the
- * InputRecord/OutputRecords still have the same functionality, except
- * that they are overridden with EngineInputRecord/EngineOutputRecord,
- * which provide SSLEngine-specific functionality.
- *
- * Some of the major differences are:
- *
- * EngineInputRecord/EngineOutputRecord/EngineWriter:
- *
- * In order to avoid writing whole new control flows for
- * handshaking, and to reuse most of the same code, we kept most
- * of the actual handshake code the same. As usual, reading
- * handshake data may trigger output of more handshake data, so
- * what we do is write this data to internal buffers, and wait for
- * wrap() to be called to give that data a ride.
- *
- * All data is routed through
- * EngineInputRecord/EngineOutputRecord. However, all handshake
- * data (ct_alert/ct_change_cipher_spec/ct_handshake) are passed
- * through to the underlying InputRecord/OutputRecord, and
- * the data uses the internal buffers.
- *
- * Application data is handled slightly different, we copy the data
- * directly from the src to the dst buffers, and do all operations
- * on those buffers, saving the overhead of multiple copies.
- *
- * In the case of an inbound record, unwrap passes the inbound
- * ByteBuffer to the InputRecord. If the data is handshake data,
- * the data is read into the InputRecord's internal buffer. If
- * the data is application data, the data is decoded directly into
- * the dst buffer.
- *
- * In the case of an outbound record, when the write to the
- * "real" OutputStream's would normally take place, instead we
- * call back up to the EngineOutputRecord's version of
- * writeBuffer, at which time we capture the resulting output in a
- * ByteBuffer, and send that back to the EngineWriter for internal
- * storage.
- *
- * EngineWriter is responsible for "handling" all outbound
- * data, be it handshake or app data, and for returning the data
- * to wrap() in the proper order.
- *
- * ClientHandshaker/ServerHandshaker/Handshaker:
- * Methods which relied on SSLSocket now have work on either
- * SSLSockets or SSLEngines.
- *
* @author Brad Wetmore
*/
final public class SSLEngineImpl extends SSLEngine {
@@ -175,11 +124,10 @@
/*
* Once we're in state cs_CLOSED, we can continue to
* wrap/unwrap until we finish sending/receiving the messages
- * for close_notify. EngineWriter handles outboundDone.
+ * for close_notify.
*/
private boolean inboundDone = false;
-
- EngineWriter writer;
+ private boolean outboundDone = false;
/*
* The authentication context holds all information used to establish
@@ -201,21 +149,6 @@
private SSLSessionImpl sess;
private volatile SSLSessionImpl handshakeSession;
-
- /*
- * Client authentication be off, requested, or required.
- *
- * This will be used by both this class and SSLSocket's variants.
- */
- static final byte clauth_none = 0;
- static final byte clauth_requested = 1;
- static final byte clauth_required = 2;
-
- /*
- * Flag indicating that the engine has received a ChangeCipherSpec message.
- */
- private boolean receivedCCS;
-
/*
* Flag indicating if the next record we receive MUST be a Finished
* message. Temporarily set during the handshake to ensure that
@@ -243,11 +176,12 @@
* Per-connection private state that doesn't change when the
* session is changed.
*/
- private byte doClientAuth;
- private boolean enableSessionCreation = true;
- EngineInputRecord inputRecord;
- EngineOutputRecord outputRecord;
- private AccessControlContext acc;
+ private ClientAuthType doClientAuth =
+ ClientAuthType.CLIENT_AUTH_NONE;
+ private boolean enableSessionCreation = true;
+ InputRecord inputRecord;
+ OutputRecord outputRecord;
+ private AccessControlContext acc;
// The cipher suites enabled for use on this connection.
private CipherSuiteList enabledCipherSuites;
@@ -280,14 +214,7 @@
/*
* The SSL version associated with this connection.
*/
- private ProtocolVersion protocolVersion = ProtocolVersion.DEFAULT;
-
- /*
- * Crypto state that's reinitialized when the session changes.
- */
- private Authenticator readAuthenticator, writeAuthenticator;
- private CipherBox readCipher, writeCipher;
- // NOTE: compression state would be saved here
+ private ProtocolVersion protocolVersion;
/*
* security parameters for secure renegotiation.
@@ -320,17 +247,27 @@
Object writeLock;
/*
- * Is it the first application record to write?
- */
- private boolean isFirstAppOutputRecord = true;
-
- /*
* Whether local cipher suites preference in server side should be
* honored during handshaking?
*/
private boolean preferLocalCipherSuites = false;
/*
+ * whether DTLS handshake retransmissions should be enabled?
+ */
+ private boolean enableRetransmissions = false;
+
+ /*
+ * The maximum expected network packet size for SSL/TLS/DTLS records.
+ */
+ private int maximumPacketSize = 0;
+
+ /*
+ * Is this an instance for Datagram Transport Layer Security (DTLS)?
+ */
+ private final boolean isDTLS;
+
+ /*
* Class and subclass dynamic debugging support
*/
private static final Debug debug = Debug.getInstance("ssl");
@@ -344,23 +281,25 @@
* host/port hints. This Engine will not be able to cache
* sessions, but must renegotiate everything by hand.
*/
- SSLEngineImpl(SSLContextImpl ctx) {
+ SSLEngineImpl(SSLContextImpl ctx, boolean isDTLS) {
super();
- init(ctx);
+ this.isDTLS = isDTLS;
+ init(ctx, isDTLS);
}
/**
* Constructor for an SSLEngine from SSLContext.
*/
- SSLEngineImpl(SSLContextImpl ctx, String host, int port) {
+ SSLEngineImpl(SSLContextImpl ctx, String host, int port, boolean isDTLS) {
super(host, port);
- init(ctx);
+ this.isDTLS = isDTLS;
+ init(ctx, isDTLS);
}
/**
* Initializes the Engine
*/
- private void init(SSLContextImpl ctx) {
+ private void init(SSLContextImpl ctx, boolean isDTLS) {
if (debug != null && Debug.isOn("ssl")) {
System.out.println("Using SSLEngineImpl.");
}
@@ -368,6 +307,8 @@
sslContext = ctx;
sess = SSLSessionImpl.nullSession;
handshakeSession = null;
+ protocolVersion = isDTLS ?
+ ProtocolVersion.DEFAULT_DTLS : ProtocolVersion.DEFAULT_TLS;
/*
* State is cs_START until we initialize the handshaker.
@@ -377,22 +318,11 @@
*/
roleIsServer = true;
connectionState = cs_START;
- receivedCCS = false;
// default server name indication
serverNames =
Utilities.addToSNIServerNameList(serverNames, getPeerHost());
- /*
- * default read and write side cipher and MAC support
- *
- * Note: compression support would go here too
- */
- readCipher = CipherBox.NULL;
- readAuthenticator = MAC.NULL;
- writeCipher = CipherBox.NULL;
- writeAuthenticator = MAC.NULL;
-
// default security parameters for secure renegotiation
secureRenegotiation = false;
clientVerifyData = new byte[0];
@@ -421,12 +351,19 @@
* elsewhere. All inbound data goes through this one
* input record.
*/
- outputRecord =
- new EngineOutputRecord(Record.ct_application_data, this);
- inputRecord = new EngineInputRecord(this);
- inputRecord.enableFormatChecks();
+ if (isDTLS) {
+ enableRetransmissions = true;
+
+ // SSLEngine needs no record local buffer
+ outputRecord = new DTLSOutputRecord();
+ inputRecord = new DTLSInputRecord();
- writer = new EngineWriter();
+ } else {
+ outputRecord = new SSLEngineOutputRecord();
+ inputRecord = new SSLEngineInputRecord();
+ }
+
+ maximumPacketSize = outputRecord.getMaxPacketSize();
}
/**
@@ -480,18 +417,23 @@
handshaker = new ServerHandshaker(this, sslContext,
enabledProtocols, doClientAuth,
protocolVersion, connectionState == cs_HANDSHAKE,
- secureRenegotiation, clientVerifyData, serverVerifyData);
+ secureRenegotiation, clientVerifyData, serverVerifyData,
+ isDTLS);
handshaker.setSNIMatchers(sniMatchers);
handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites);
} else {
handshaker = new ClientHandshaker(this, sslContext,
enabledProtocols,
protocolVersion, connectionState == cs_HANDSHAKE,
- secureRenegotiation, clientVerifyData, serverVerifyData);
+ secureRenegotiation, clientVerifyData, serverVerifyData,
+ isDTLS);
handshaker.setSNIServerNames(serverNames);
}
+ handshaker.setMaximumPacketSize(maximumPacketSize);
handshaker.setEnabledCipherSuites(enabledCipherSuites);
handshaker.setEnableSessionCreation(enableSessionCreation);
+
+ outputRecord.initHandshaker();
}
/*
@@ -504,11 +446,14 @@
}
synchronized (this) {
- if (writer.hasOutboundData()) {
+ if (!outputRecord.isEmpty()) {
+ // If no handshaking, special case to wrap alters.
return HandshakeStatus.NEED_WRAP;
} else if (handshaker != null) {
if (handshaker.taskOutstanding()) {
return HandshakeStatus.NEED_TASK;
+ } else if (isDTLS && !inputRecord.isEmpty()) {
+ return HandshakeStatus.NEED_UNWRAP_AGAIN;
} else {
return HandshakeStatus.NEED_UNWRAP;
}
@@ -572,67 +517,15 @@
}
/*
- * When a connection finishes handshaking by enabling use of a newly
- * negotiated session, each end learns about it in two halves (read,
- * and write). When both read and write ciphers have changed, and the
- * last handshake message has been read, the connection has joined
- * (rejoined) the new session.
- *
- * NOTE: The SSLv3 spec is rather unclear on the concepts here.
- * Sessions don't change once they're established (including cipher
- * suite and master secret) but connections can join them (and leave
- * them). They're created by handshaking, though sometime handshaking
- * causes connections to join up with pre-established sessions.
- *
- * Synchronized on "this" from readRecord.
- */
- private void changeReadCiphers() throws SSLException {
- if (connectionState != cs_HANDSHAKE
- && connectionState != cs_RENEGOTIATE) {
- throw new SSLProtocolException(
- "State error, change cipher specs");
- }
-
- // ... create decompressor
-
- CipherBox oldCipher = readCipher;
-
- try {
- readCipher = handshaker.newReadCipher();
- readAuthenticator = handshaker.newReadAuthenticator();
- } catch (GeneralSecurityException e) {
- // "can't happen"
- throw new SSLException("Algorithm missing: ", e);
- }
-
- /*
- * Dispose of any intermediate state in the underlying cipher.
- * For PKCS11 ciphers, this will release any attached sessions,
- * and thus make finalization faster.
- *
- * Since MAC's doFinal() is called for every SSL/TLS packet, it's
- * not necessary to do the same with MAC's.
- */
- oldCipher.dispose();
- }
-
- /*
* used by Handshaker to change the active write cipher, follows
* the output of the CCS message.
*
* Also synchronized on "this" from readRecord/delegatedTask.
*/
- void changeWriteCiphers() throws SSLException {
- if (connectionState != cs_HANDSHAKE
- && connectionState != cs_RENEGOTIATE) {
- throw new SSLProtocolException(
- "State error, change cipher specs");
- }
+ void changeWriteCiphers() throws IOException {
- // ... create compressor
-
- CipherBox oldCipher = writeCipher;
-
+ Authenticator writeAuthenticator;
+ CipherBox writeCipher;
try {
writeCipher = handshaker.newWriteCipher();
writeAuthenticator = handshaker.newWriteAuthenticator();
@@ -641,11 +534,7 @@
throw new SSLException("Algorithm missing: ", e);
}
- // See comment above.
- oldCipher.dispose();
-
- // reset the flag of the first application record
- isFirstAppOutputRecord = true;
+ outputRecord.changeWriteCiphers(writeAuthenticator, writeCipher);
}
/*
@@ -716,10 +605,6 @@
//
// Kickstart handshake state machine if we need to ...
//
- // Note that handshaker.kickstart() writes the message
- // to its HandshakeOutStream, which calls back into
- // SSLSocketImpl.writeRecord() to send it.
- //
if (!handshaker.activated()) {
// prior to handshaking, activate the handshake
if (connectionState == cs_RENEGOTIATE) {
@@ -738,10 +623,6 @@
} else {
// we want to renegotiate, send hello request
handshaker.kickstart();
-
- // hello request is not included in the handshake
- // hashes, reset them
- handshaker.handshakeHash.reset();
}
}
}
@@ -771,15 +652,20 @@
* the unwrapLock, which blocks multiple unwraps from occurring.
*/
@Override
- public SSLEngineResult unwrap(ByteBuffer netData, ByteBuffer [] appData,
+ public SSLEngineResult unwrap(ByteBuffer netData, ByteBuffer[] appData,
int offset, int length) throws SSLException {
- EngineArgs ea = new EngineArgs(netData, appData, offset, length);
+ // check engine parameters
+ checkEngineParas(netData, appData, offset, length, false);
try {
synchronized (unwrapLock) {
- return readNetRecord(ea);
+ return readNetRecord(netData, appData, offset, length);
}
+ } catch (SSLProtocolException spe) {
+ // may be an unexpected handshake message
+ fatal(Alerts.alert_unexpected_message, spe.getMessage(), spe);
+ return null; // make compiler happy
} catch (Exception e) {
/*
* Don't reset position so it looks like we didn't
@@ -790,11 +676,39 @@
fatal(Alerts.alert_internal_error,
"problem unwrapping net record", e);
return null; // make compiler happy
- } finally {
+ }
+ }
+
+ private static void checkEngineParas(ByteBuffer netData,
+ ByteBuffer[] appData, int offset, int len, boolean isForWrap) {
+
+ if ((netData == null) || (appData == null)) {
+ throw new IllegalArgumentException("src/dst is null");
+ }
+
+ if ((offset < 0) || (len < 0) || (offset > appData.length - len)) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ /*
+ * If wrapping, make sure the destination bufffer is writable.
+ */
+ if (isForWrap && netData.isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+
+ for (int i = offset; i < offset + len; i++) {
+ if (appData[i] == null) {
+ throw new IllegalArgumentException(
+ "appData[" + i + "] == null");
+ }
+
/*
- * Just in case something failed to reset limits properly.
+ * If unwrapping, make sure the destination bufffers are writable.
*/
- ea.resetLim();
+ if (!isForWrap && appData[i].isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
}
}
@@ -802,7 +716,8 @@
* Makes additional checks for unwrap, but this time more
* specific to this packet and the current state of the machine.
*/
- private SSLEngineResult readNetRecord(EngineArgs ea) throws IOException {
+ private SSLEngineResult readNetRecord(ByteBuffer netData,
+ ByteBuffer[] appData, int offset, int length) throws IOException {
Status status = null;
HandshakeStatus hsStatus = null;
@@ -857,54 +772,117 @@
* message which would change the ciphers.
*/
if (hsStatus == HandshakeStatus.NEED_TASK) {
+ return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
+ }
+
+ if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
+ Plaintext plainText = null;
+ try {
+ plainText = readRecord(null, null, 0, 0);
+ } catch (SSLException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new SSLException("readRecord", e);
+ }
+
+ status = (isInboundDone() ? Status.CLOSED : Status.OK);
+ hsStatus = getHSStatus(plainText.handshakeStatus);
+
return new SSLEngineResult(
- Status.OK, hsStatus, 0, 0);
+ status, hsStatus, 0, 0, plainText.recordSN);
}
/*
* Check the packet to make sure enough is here.
* This will also indirectly check for 0 len packets.
*/
- int packetLen = inputRecord.bytesInCompletePacket(ea.netData);
+ int packetLen = 0;
+ try {
+ packetLen = inputRecord.bytesInCompletePacket(netData);
+ } catch (SSLException ssle) {
+ // Need to discard invalid records for DTLS protocols.
+ if (isDTLS) {
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(
+ Thread.currentThread().getName() +
+ " discard invalid record: " + ssle);
+ }
+
+ // invalid, discard the entire data [section 4.1.2.7, RFC 6347]
+ int deltaNet = netData.remaining();
+ netData.position(netData.limit());
+
+ status = (isInboundDone() ? Status.CLOSED : Status.OK);
+ hsStatus = getHSStatus(hsStatus);
+
+ return new SSLEngineResult(status, hsStatus, deltaNet, 0, -1L);
+ } else {
+ throw ssle;
+ }
+ }
// Is this packet bigger than SSL/TLS normally allows?
if (packetLen > sess.getPacketBufferSize()) {
- if (packetLen > Record.maxLargeRecordSize) {
- throw new SSLProtocolException(
- "Input SSL/TLS record too big: max = " +
- Record.maxLargeRecordSize +
- " len = " + packetLen);
- } else {
+ int largestRecordSize = isDTLS ?
+ DTLSRecord.maxRecordSize : SSLRecord.maxLargeRecordSize;
+ if ((packetLen <= largestRecordSize) && !isDTLS) {
// Expand the expected maximum packet/application buffer
// sizes.
+ //
+ // Only apply to SSL/TLS protocols.
+
+ // Old behavior: shall we honor the System Property
+ // "jsse.SSLEngine.acceptLargeFragments" if it is "false"?
sess.expandBufferSizes();
}
+
+ // check the packet again
+ largestRecordSize = sess.getPacketBufferSize();
+ if (packetLen > largestRecordSize) {
+ throw new SSLProtocolException(
+ "Input record too big: max = " +
+ largestRecordSize + " len = " + packetLen);
+ }
+ }
+
+ int netPos = netData.position();
+ int appRemains = 0;
+ for (int i = offset; i < offset + length; i++) {
+ if (appData[i] == null) {
+ throw new IllegalArgumentException(
+ "appData[" + i + "] == null");
+ }
+ appRemains += appData[i].remaining();
}
/*
* Check for OVERFLOW.
*
- * To be considered: We could delay enforcing the application buffer
- * free space requirement until after the initial handshaking.
+ * Delay enforcing the application buffer free space requirement
+ * until after the initial handshaking.
*/
- if ((packetLen - Record.headerSize) > ea.getAppRemaining()) {
- return new SSLEngineResult(Status.BUFFER_OVERFLOW, hsStatus, 0, 0);
+ // synchronize connectionState?
+ if ((connectionState == cs_DATA) ||
+ (connectionState == cs_RENEGOTIATE)) {
+
+ int FragLen = inputRecord.estimateFragmentSize(packetLen);
+ if (FragLen > appRemains) {
+ return new SSLEngineResult(
+ Status.BUFFER_OVERFLOW, hsStatus, 0, 0);
+ }
}
// check for UNDERFLOW.
- if ((packetLen == -1) || (ea.netData.remaining() < packetLen)) {
- return new SSLEngineResult(
- Status.BUFFER_UNDERFLOW, hsStatus, 0, 0);
+ if ((packetLen == -1) || (netData.remaining() < packetLen)) {
+ return new SSLEngineResult(Status.BUFFER_UNDERFLOW, hsStatus, 0, 0);
}
/*
* We're now ready to actually do the read.
- * The only result code we really need to be exactly
- * right is the HS finished, for signaling to
- * HandshakeCompletedListeners.
*/
+ Plaintext plainText = null;
try {
- hsStatus = readRecord(ea);
+ plainText = readRecord(netData, appData, offset, length);
} catch (SSLException e) {
throw e;
} catch (IOException e) {
@@ -922,10 +900,21 @@
* status above should cover: FINISHED, NEED_TASK
*/
status = (isInboundDone() ? Status.CLOSED : Status.OK);
- hsStatus = getHSStatus(hsStatus);
+ hsStatus = getHSStatus(plainText.handshakeStatus);
+
+ int deltaNet = netData.position() - netPos;
+ int deltaApp = appRemains;
+ for (int i = offset; i < offset + length; i++) {
+ deltaApp -= appData[i].remaining();
+ }
- return new SSLEngineResult(status, hsStatus,
- ea.deltaNet(), ea.deltaApp());
+ return new SSLEngineResult(
+ status, hsStatus, deltaNet, deltaApp, plainText.recordSN);
+ }
+
+ // the caller have synchronized readLock
+ void expectingFinishFlight() {
+ inputRecord.expectingFinishFlight();
}
/*
@@ -936,220 +925,266 @@
* that a handshake just completed.
*
* It would be nice to be symmetrical with the write side and move
- * the majority of this to EngineInputRecord, but there's too much
+ * the majority of this to SSLInputRecord, but there's too much
* SSLEngine state to do that cleanly. It must still live here.
*/
- private HandshakeStatus readRecord(EngineArgs ea) throws IOException {
-
- HandshakeStatus hsStatus = null;
+ private Plaintext readRecord(ByteBuffer netData,
+ ByteBuffer[] appData, int offset, int length) throws IOException {
/*
* The various operations will return new sliced BB's,
* this will avoid having to worry about positions and
* limits in the netBB.
*/
- ByteBuffer readBB = null;
- ByteBuffer decryptedBB = null;
+ Plaintext plainText = null;
- if (getConnectionState() != cs_ERROR) {
+ if (getConnectionState() == cs_ERROR) {
+ return Plaintext.PLAINTEXT_NULL;
+ }
- /*
- * Read a record ... maybe emitting an alert if we get a
- * comprehensible but unsupported "hello" message during
- * format checking (e.g. V2).
- */
- try {
- readBB = inputRecord.read(ea.netData);
- } catch (IOException e) {
- fatal(Alerts.alert_unexpected_message, e);
+ /*
+ * Read a record ... maybe emitting an alert if we get a
+ * comprehensible but unsupported "hello" message during
+ * format checking (e.g. V2).
+ */
+ try {
+ if (isDTLS) {
+ // Don't process the incoming record until all of the
+ // buffered records get handled.
+ plainText = inputRecord.acquirePlaintext();
}
+ if ((!isDTLS || plainText == null) && netData != null) {
+ plainText = inputRecord.decode(netData);
+ }
+ } catch (UnsupportedOperationException unsoe) { // SSLv2Hello
+ // Hack code to deliver SSLv2 error message for SSL/TLS connections.
+ if (!isDTLS) {
+ outputRecord.encodeV2NoCipher();
+ }
+
+ fatal(Alerts.alert_unexpected_message, unsoe);
+ } catch (BadPaddingException e) {
/*
* The basic SSLv3 record protection involves (optional)
* encryption for privacy, and an integrity check ensuring
* data origin authentication. We do them both here, and
* throw a fatal alert if the integrity check fails.
*/
- try {
- decryptedBB = inputRecord.decrypt(
- readAuthenticator, readCipher, readBB);
- } catch (BadPaddingException e) {
- byte alertType = (inputRecord.contentType() ==
- Record.ct_handshake) ?
- Alerts.alert_handshake_failure :
- Alerts.alert_bad_record_mac;
- fatal(alertType, e.getMessage(), e);
- }
+ byte alertType = (connectionState != cs_DATA) ?
+ Alerts.alert_handshake_failure :
+ Alerts.alert_bad_record_mac;
+ fatal(alertType, e.getMessage(), e);
+ } catch (SSLHandshakeException she) {
+ // may be record sequence number overflow
+ fatal(Alerts.alert_handshake_failure, she);
+ } catch (IOException ioe) {
+ fatal(Alerts.alert_unexpected_message, ioe);
+ }
+
+ // plainText should never be null for TLS protocols
+ HandshakeStatus hsStatus = null;
+ if (!isDTLS || plainText != null) {
+ hsStatus = processInputRecord(plainText, appData, offset, length);
+ }
- // if (!inputRecord.decompress(c))
- // fatal(Alerts.alert_decompression_failure,
- // "decompression failure");
+ if (hsStatus == null) {
+ hsStatus = getHSStatus(null);
+ }
+
+ if (plainText == null) {
+ plainText = new Plaintext();
+ }
+ plainText.handshakeStatus = hsStatus;
+ return plainText;
+ }
- /*
- * Process the record.
- */
+ /*
+ * Process the record.
+ */
+ private synchronized HandshakeStatus processInputRecord(
+ Plaintext plainText,
+ ByteBuffer[] appData, int offset, int length) throws IOException {
- synchronized (this) {
- switch (inputRecord.contentType()) {
- case Record.ct_handshake:
- /*
- * Handshake messages always go to a pending session
- * handshaker ... if there isn't one, create one. This
- * must work asynchronously, for renegotiation.
- *
- * NOTE that handshaking will either resume a session
- * which was in the cache (and which might have other
- * connections in it already), or else will start a new
- * session (new keys exchanged) with just this connection
- * in it.
- */
- initHandshaker();
- if (!handshaker.activated()) {
- // prior to handshaking, activate the handshake
- if (connectionState == cs_RENEGOTIATE) {
- // don't use SSLv2Hello when renegotiating
- handshaker.activate(protocolVersion);
- } else {
- handshaker.activate(null);
- }
+ HandshakeStatus hsStatus = null;
+ switch (plainText.contentType) {
+ case Record.ct_handshake:
+ /*
+ * Handshake messages always go to a pending session
+ * handshaker ... if there isn't one, create one. This
+ * must work asynchronously, for renegotiation.
+ *
+ * NOTE that handshaking will either resume a session
+ * which was in the cache (and which might have other
+ * connections in it already), or else will start a new
+ * session (new keys exchanged) with just this connection
+ * in it.
+ */
+ initHandshaker();
+ if (!handshaker.activated()) {
+ // prior to handshaking, activate the handshake
+ if (connectionState == cs_RENEGOTIATE) {
+ // don't use SSLv2Hello when renegotiating
+ handshaker.activate(protocolVersion);
+ } else {
+ handshaker.activate(null);
+ }
+ }
+
+ /*
+ * process the handshake record ... may contain just
+ * a partial handshake message or multiple messages.
+ *
+ * The handshaker state machine will ensure that it's
+ * a finished message.
+ */
+ handshaker.processRecord(plainText.fragment, expectingFinished);
+ expectingFinished = false;
+
+ if (handshaker.invalidated) {
+ finishHandshake();
+
+ // if state is cs_RENEGOTIATE, revert it to cs_DATA
+ if (connectionState == cs_RENEGOTIATE) {
+ connectionState = cs_DATA;
+ }
+ } else if (handshaker.isDone()) {
+ // reset the parameters for secure renegotiation.
+ secureRenegotiation =
+ handshaker.isSecureRenegotiation();
+ clientVerifyData = handshaker.getClientVerifyData();
+ serverVerifyData = handshaker.getServerVerifyData();
+
+ sess = handshaker.getSession();
+ handshakeSession = null;
+ if (outputRecord.isEmpty()) {
+ hsStatus = finishHandshake();
+ connectionState = cs_DATA;
}
- /*
- * process the handshake record ... may contain just
- * a partial handshake message or multiple messages.
- *
- * The handshaker state machine will ensure that it's
- * a finished message.
- */
- handshaker.process_record(inputRecord, expectingFinished);
- expectingFinished = false;
+ // No handshakeListeners here. That's a
+ // SSLSocket thing.
+ } else if (handshaker.taskOutstanding()) {
+ hsStatus = HandshakeStatus.NEED_TASK;
+ }
+ break;
- if (handshaker.invalidated) {
- handshaker = null;
- receivedCCS = false;
- // if state is cs_RENEGOTIATE, revert it to cs_DATA
- if (connectionState == cs_RENEGOTIATE) {
- connectionState = cs_DATA;
- }
- } else if (handshaker.isDone()) {
- // reset the parameters for secure renegotiation.
- secureRenegotiation =
- handshaker.isSecureRenegotiation();
- clientVerifyData = handshaker.getClientVerifyData();
- serverVerifyData = handshaker.getServerVerifyData();
-
- sess = handshaker.getSession();
- handshakeSession = null;
- if (!writer.hasOutboundData()) {
- hsStatus = HandshakeStatus.FINISHED;
- }
- handshaker = null;
- connectionState = cs_DATA;
- receivedCCS = false;
-
- // No handshakeListeners here. That's a
- // SSLSocket thing.
- } else if (handshaker.taskOutstanding()) {
- hsStatus = HandshakeStatus.NEED_TASK;
- }
- break;
-
- case Record.ct_application_data:
- // Pass this right back up to the application.
- if ((connectionState != cs_DATA)
- && (connectionState != cs_RENEGOTIATE)
- && (connectionState != cs_CLOSED)) {
- throw new SSLProtocolException(
+ case Record.ct_application_data:
+ // Pass this right back up to the application.
+ if ((connectionState != cs_DATA)
+ && (connectionState != cs_RENEGOTIATE)
+ && (connectionState != cs_CLOSED)) {
+ throw new SSLProtocolException(
"Data received in non-data state: " +
connectionState);
- }
-
- if (expectingFinished) {
- throw new SSLProtocolException
- ("Expecting finished message, received data");
- }
-
- /*
- * Don't return data once the inbound side is
- * closed.
- */
- if (!inboundDone) {
- ea.scatter(decryptedBB.slice());
- }
- break;
-
- case Record.ct_alert:
- recvAlert();
- break;
+ }
- case Record.ct_change_cipher_spec:
- if ((connectionState != cs_HANDSHAKE
- && connectionState != cs_RENEGOTIATE)
- || !handshaker.sessionKeysCalculated()
- || receivedCCS) {
- // For the CCS message arriving in the wrong state
- fatal(Alerts.alert_unexpected_message,
- "illegal change cipher spec msg, conn state = "
- + connectionState + ", handshake state = "
- + handshaker.state);
- } else if (inputRecord.available() != 1
- || inputRecord.read() != 1) {
- // For structural/content issues with the CCS
- fatal(Alerts.alert_unexpected_message,
- "Malformed change cipher spec msg");
- }
-
- // Once we've received CCS, update the flag.
- // If the remote endpoint sends it again in this handshake
- // we won't process it.
- receivedCCS = true;
+ if (expectingFinished) {
+ throw new SSLProtocolException
+ ("Expecting finished message, received data");
+ }
- //
- // The first message after a change_cipher_spec
- // record MUST be a "Finished" handshake record,
- // else it's a protocol violation. We force this
- // to be checked by a minor tweak to the state
- // machine.
- //
- changeReadCiphers();
- // next message MUST be a finished message
- expectingFinished = true;
- break;
+ if (!inboundDone) {
+ ByteBuffer fragment = plainText.fragment;
+ int remains = fragment.remaining();
- default:
- //
- // TLS requires that unrecognized records be ignored.
- //
- if (debug != null && Debug.isOn("ssl")) {
- System.out.println(Thread.currentThread().getName() +
- ", Received record type: "
- + inputRecord.contentType());
- }
- break;
- } // switch
-
- /*
- * We only need to check the sequence number state for
- * non-handshaking record.
- *
- * Note that in order to maintain the handshake status
- * properly, we check the sequence number after the last
- * record reading process. As we request renegotiation
- * or close the connection for wrapped sequence number
- * when there is enough sequence number space left to
- * handle a few more records, so the sequence number
- * of the last record cannot be wrapped.
- */
- hsStatus = getHSStatus(hsStatus);
- if (connectionState < cs_ERROR && !isInboundDone() &&
- (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) {
- if (checkSequenceNumber(readAuthenticator,
- inputRecord.contentType())) {
- hsStatus = getHSStatus(null);
+ // Should have enough room in appData.
+ for (int i = offset;
+ ((i < (offset + length)) && (remains > 0)); i++) {
+ int amount = Math.min(appData[i].remaining(), remains);
+ fragment.limit(fragment.position() + amount);
+ appData[i].put(fragment);
+ remains -= amount;
}
}
- } // synchronized (this)
+
+ break;
+
+ case Record.ct_alert:
+ recvAlert(plainText.fragment);
+ break;
+
+ case Record.ct_change_cipher_spec:
+ if ((connectionState != cs_HANDSHAKE
+ && connectionState != cs_RENEGOTIATE)) {
+ // For the CCS message arriving in the wrong state
+ fatal(Alerts.alert_unexpected_message,
+ "illegal change cipher spec msg, conn state = "
+ + connectionState);
+ } else if (plainText.fragment.remaining() != 1
+ || plainText.fragment.get() != 1) {
+ // For structural/content issues with the CCS
+ fatal(Alerts.alert_unexpected_message,
+ "Malformed change cipher spec msg");
+ }
+
+ //
+ // The first message after a change_cipher_spec
+ // record MUST be a "Finished" handshake record,
+ // else it's a protocol violation. We force this
+ // to be checked by a minor tweak to the state
+ // machine.
+ //
+ handshaker.receiveChangeCipherSpec();
+
+ CipherBox readCipher;
+ Authenticator readAuthenticator;
+ try {
+ readCipher = handshaker.newReadCipher();
+ readAuthenticator = handshaker.newReadAuthenticator();
+ } catch (GeneralSecurityException e) {
+ // can't happen
+ throw new SSLException("Algorithm missing: ", e);
+ }
+ inputRecord.changeReadCiphers(readAuthenticator, readCipher);
+
+ // next message MUST be a finished message
+ expectingFinished = true;
+ break;
+
+ default:
+ //
+ // TLS requires that unrecognized records be ignored.
+ //
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", Received record type: " + plainText.contentType);
+ }
+ break;
+ } // switch
+
+ /*
+ * We only need to check the sequence number state for
+ * non-handshaking record.
+ *
+ * Note that in order to maintain the handshake status
+ * properly, we check the sequence number after the last
+ * record reading process. As we request renegotiation
+ * or close the connection for wrapped sequence number
+ * when there is enough sequence number space left to
+ * handle a few more records, so the sequence number
+ * of the last record cannot be wrapped.
+ */
+ hsStatus = getHSStatus(hsStatus);
+ if (connectionState < cs_ERROR && !isInboundDone() &&
+ (hsStatus == HandshakeStatus.NOT_HANDSHAKING) &&
+ (inputRecord.seqNumIsHuge())) {
+ /*
+ * Ask for renegotiation when need to renew sequence number.
+ *
+ * Don't bother to kickstart the renegotiation when the local is
+ * asking for it.
+ */
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", request renegotiation " +
+ "to avoid sequence number overflow");
+ }
+
+ beginHandshake();
+
+ hsStatus = getHSStatus(null);
}
return hsStatus;
@@ -1166,36 +1201,33 @@
* the wrapLock, which blocks multiple wraps from occurring.
*/
@Override
- public SSLEngineResult wrap(ByteBuffer [] appData,
+ public SSLEngineResult wrap(ByteBuffer[] appData,
int offset, int length, ByteBuffer netData) throws SSLException {
- EngineArgs ea = new EngineArgs(appData, offset, length, netData);
+ // check engine parameters
+ checkEngineParas(netData, appData, offset, length, true);
/*
* We can be smarter about using smaller buffer sizes later.
- * For now, force it to be large enough to handle any
- * valid SSL/TLS record.
+ * For now, force it to be large enough to handle any valid record.
*/
- if (netData.remaining() < EngineOutputRecord.maxRecordSize) {
+ if (netData.remaining() < sess.getPacketBufferSize()) {
return new SSLEngineResult(
Status.BUFFER_OVERFLOW, getHSStatus(null), 0, 0);
}
try {
synchronized (wrapLock) {
- return writeAppRecord(ea);
+ return writeAppRecord(appData, offset, length, netData);
}
+ } catch (SSLProtocolException spe) {
+ // may be an unexpected handshake message
+ fatal(Alerts.alert_unexpected_message, spe.getMessage(), spe);
+ return null; // make compiler happy
} catch (Exception e) {
- ea.resetPos();
-
fatal(Alerts.alert_internal_error,
"problem wrapping app data", e);
return null; // make compiler happy
- } finally {
- /*
- * Just in case something didn't reset limits properly.
- */
- ea.resetLim();
}
}
@@ -1203,7 +1235,8 @@
* Makes additional checks for unwrap, but this time more
* specific to this packet and the current state of the machine.
*/
- private SSLEngineResult writeAppRecord(EngineArgs ea) throws IOException {
+ private SSLEngineResult writeAppRecord(ByteBuffer[] appData,
+ int offset, int length, ByteBuffer netData) throws IOException {
Status status = null;
HandshakeStatus hsStatus = null;
@@ -1216,7 +1249,7 @@
/*
* short circuit if we're closed/closing.
*/
- if (writer.isOutboundDone()) {
+ if (isOutboundDone()) {
return new SSLEngineResult(Status.CLOSED, getHSStatus(null), 0, 0);
}
@@ -1226,7 +1259,8 @@
*/
synchronized (this) {
if ((connectionState == cs_HANDSHAKE) ||
- (connectionState == cs_START)) {
+ (connectionState == cs_START)) {
+
kickstartHandshake();
/*
@@ -1234,9 +1268,18 @@
* without trying to wrap anything.
*/
hsStatus = getHSStatus(null);
+ if (hsStatus == HandshakeStatus.NEED_UNWRAP) {
+ /*
+ * For DTLS, if the handshake state is
+ * HandshakeStatus.NEED_UNWRAP, a call to SSLEngine.wrap()
+ * means that the previous handshake packets (if delivered)
+ * get lost, and need retransmit the handshake messages.
+ */
+ if (!isDTLS || !enableRetransmissions ||
+ (handshaker == null) || outputRecord.firstMessage) {
- if (hsStatus == HandshakeStatus.NEED_UNWRAP) {
- return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
+ return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
+ } // otherwise, need retransmission
}
}
}
@@ -1258,17 +1301,33 @@
* message which would change the ciphers.
*/
if (hsStatus == HandshakeStatus.NEED_TASK) {
- return new SSLEngineResult(
- Status.OK, hsStatus, 0, 0);
+ return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
}
/*
* This will obtain any waiting outbound data, or will
* process the outbound appData.
*/
+ int netPos = netData.position();
+ int appRemains = 0;
+ for (int i = offset; i < offset + length; i++) {
+ if (appData[i] == null) {
+ throw new IllegalArgumentException(
+ "appData[" + i + "] == null");
+ }
+ appRemains += appData[i].remaining();
+ }
+
+ Ciphertext ciphertext = null;
try {
- synchronized (writeLock) {
- hsStatus = writeRecord(outputRecord, ea);
+ if (appRemains != 0) {
+ synchronized (writeLock) {
+ ciphertext = writeRecord(appData, offset, length, netData);
+ }
+ } else {
+ synchronized (writeLock) {
+ ciphertext = writeRecord(null, 0, 0, netData);
+ }
}
} catch (SSLException e) {
throw e;
@@ -1283,21 +1342,62 @@
* status above should cover: NEED_WRAP/FINISHED
*/
status = (isOutboundDone() ? Status.CLOSED : Status.OK);
- hsStatus = getHSStatus(hsStatus);
+ hsStatus = getHSStatus(ciphertext.handshakeStatus);
- return new SSLEngineResult(status, hsStatus,
- ea.deltaApp(), ea.deltaNet());
+ int deltaNet = netData.position() - netPos;
+ int deltaApp = appRemains;
+ for (int i = offset; i < offset + length; i++) {
+ deltaApp -= appData[i].remaining();
+ }
+
+ return new SSLEngineResult(
+ status, hsStatus, deltaApp, deltaNet, ciphertext.recordSN);
}
/*
* Central point to write/get all of the outgoing data.
*/
- private HandshakeStatus writeRecord(EngineOutputRecord eor,
- EngineArgs ea) throws IOException {
+ private Ciphertext writeRecord(ByteBuffer[] appData,
+ int offset, int length, ByteBuffer netData) throws IOException {
+
+ Ciphertext ciphertext = null;
+ try {
+ // Acquire the buffered to-be-delivered records or retransmissions.
+ //
+ // May have buffered records, or need retransmission if handshaking.
+ if (!outputRecord.isEmpty() || (handshaker != null)) {
+ ciphertext = outputRecord.acquireCiphertext(netData);
+ }
+
+ if ((ciphertext == null) && (appData != null)) {
+ ciphertext = outputRecord.encode(
+ appData, offset, length, netData);
+ }
+ } catch (SSLHandshakeException she) {
+ // may be record sequence number overflow
+ fatal(Alerts.alert_handshake_failure, she);
- // eventually compress as well.
- HandshakeStatus hsStatus =
- writer.writeRecord(eor, ea, writeAuthenticator, writeCipher);
+ return Ciphertext.CIPHERTEXT_NULL; // make the complier happy
+ } catch (IOException e) {
+ fatal(Alerts.alert_unexpected_message, e);
+
+ return Ciphertext.CIPHERTEXT_NULL; // make the complier happy
+ }
+
+ if (ciphertext == null) {
+ return Ciphertext.CIPHERTEXT_NULL;
+ }
+
+ HandshakeStatus hsStatus = null;
+ Ciphertext.RecordType recordType = ciphertext.recordType;
+ if ((handshaker != null) &&
+ (recordType.contentType == Record.ct_handshake) &&
+ (recordType.handshakeType == HandshakeMessage.ht_finished) &&
+ handshaker.isDone() && outputRecord.isEmpty()) {
+
+ hsStatus = finishHandshake();
+ connectionState = cs_DATA;
+ } // Otherwise, the followed call to getHSStatus() will help.
/*
* We only need to check the sequence number state for
@@ -1313,129 +1413,41 @@
*/
hsStatus = getHSStatus(hsStatus);
if (connectionState < cs_ERROR && !isOutboundDone() &&
- (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) {
- if (checkSequenceNumber(writeAuthenticator, eor.contentType())) {
- hsStatus = getHSStatus(null);
- }
- }
-
- /*
- * turn off the flag of the first application record if we really
- * consumed at least byte.
- */
- if (isFirstAppOutputRecord && ea.deltaApp() > 0) {
- isFirstAppOutputRecord = false;
- }
-
- return hsStatus;
- }
-
- /*
- * Need to split the payload except the following cases:
- *
- * 1. protocol version is TLS 1.1 or later;
- * 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
- * 3. the payload is the first application record of a freshly
- * negotiated TLS session.
- * 4. the CBC protection is disabled;
- *
- * More details, please refer to
- * EngineOutputRecord.write(EngineArgs, MAC, CipherBox).
- */
- boolean needToSplitPayload(CipherBox cipher, ProtocolVersion protocol) {
- return (protocol.v <= ProtocolVersion.TLS10.v) &&
- cipher.isCBCMode() && !isFirstAppOutputRecord &&
- Record.enableCBCProtection;
- }
-
- /*
- * Non-application OutputRecords go through here.
- */
- void writeRecord(EngineOutputRecord eor) throws IOException {
- // eventually compress as well.
- writer.writeRecord(eor, writeAuthenticator, writeCipher);
-
- /*
- * Check the sequence number state
- *
- * Note that in order to maintain the connection I/O
- * properly, we check the sequence number after the last
- * record writing process. As we request renegotiation
- * or close the connection for wrapped sequence number
- * when there is enough sequence number space left to
- * handle a few more records, so the sequence number
- * of the last record cannot be wrapped.
- */
- if ((connectionState < cs_ERROR) && !isOutboundDone()) {
- checkSequenceNumber(writeAuthenticator, eor.contentType());
- }
- }
-
- //
- // Close code
- //
-
- /**
- * Check the sequence number state
- *
- * RFC 4346 states that, "Sequence numbers are of type uint64 and
- * may not exceed 2^64-1. Sequence numbers do not wrap. If a TLS
- * implementation would need to wrap a sequence number, it must
- * renegotiate instead."
- *
- * Return true if the handshake status may be changed.
- */
- private boolean checkSequenceNumber(Authenticator authenticator, byte type)
- throws IOException {
-
- /*
- * Don't bother to check the sequence number for error or
- * closed connections, or NULL MAC
- */
- if (connectionState >= cs_ERROR || authenticator == MAC.NULL) {
- return false;
- }
-
- /*
- * Conservatively, close the connection immediately when the
- * sequence number is close to overflow
- */
- if (authenticator.seqNumOverflow()) {
+ (hsStatus == HandshakeStatus.NOT_HANDSHAKING) &&
+ (outputRecord.seqNumIsHuge())) {
/*
- * TLS protocols do not define a error alert for sequence
- * number overflow. We use handshake_failure error alert
- * for handshaking and bad_record_mac for other records.
+ * Ask for renegotiation when need to renew sequence number.
+ *
+ * Don't bother to kickstart the renegotiation when the local is
+ * asking for it.
*/
if (debug != null && Debug.isOn("ssl")) {
System.out.println(Thread.currentThread().getName() +
- ", sequence number extremely close to overflow " +
- "(2^64-1 packets). Closing connection.");
- }
-
- fatal(Alerts.alert_handshake_failure, "sequence number overflow");
-
- return true; // make the compiler happy
- }
-
- /*
- * Ask for renegotiation when need to renew sequence number.
- *
- * Don't bother to kickstart the renegotiation when the local is
- * asking for it.
- */
- if ((type != Record.ct_handshake) && authenticator.seqNumIsHuge()) {
- if (debug != null && Debug.isOn("ssl")) {
- System.out.println(Thread.currentThread().getName() +
", request renegotiation " +
"to avoid sequence number overflow");
}
beginHandshake();
- return true;
+
+ hsStatus = getHSStatus(null);
}
+ ciphertext.handshakeStatus = hsStatus;
+
+ return ciphertext;
+ }
- return false;
- }
+ private HandshakeStatus finishHandshake() {
+ handshaker = null;
+ inputRecord.setHandshakeHash(null);
+ outputRecord.setHandshakeHash(null);
+ connectionState = cs_DATA;
+
+ return HandshakeStatus.FINISHED;
+ }
+
+ //
+ // Close code
+ //
/**
* Signals that no more outbound application data will be sent
@@ -1451,7 +1463,7 @@
/*
* Already closed, ignore
*/
- if (writer.isOutboundDone()) {
+ if (outboundDone) {
return;
}
@@ -1461,7 +1473,18 @@
* If we haven't even started yet, don't bother reading inbound.
*/
case cs_START:
- writer.closeOutbound();
+ try {
+ outputRecord.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
+ outboundDone = true;
+
+ try {
+ inputRecord.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
inboundDone = true;
break;
@@ -1477,13 +1500,15 @@
// case cs_RENEGOTIATE:
default:
warning(Alerts.alert_close_notify);
- writer.closeOutbound();
+ try {
+ outputRecord.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
+ outboundDone = true;
break;
}
- // See comment in changeReadCiphers()
- writeCipher.dispose();
-
connectionState = cs_CLOSED;
}
@@ -1505,7 +1530,7 @@
*/
@Override
public boolean isOutboundDone() {
- return writer.isOutboundDone();
+ return outboundDone && outputRecord.isEmpty();
}
/**
@@ -1527,11 +1552,14 @@
}
closeOutboundInternal();
+
+ try {
+ inputRecord.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
inboundDone = true;
- // See comment in changeReadCiphers()
- readCipher.dispose();
-
connectionState = cs_CLOSED;
}
@@ -1602,6 +1630,10 @@
}
synchronized void setHandshakeSession(SSLSessionImpl session) {
+ // update the fragment size, which may be negotiated during handshaking
+ inputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize());
+ outputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize());
+
handshakeSession = session;
}
@@ -1701,6 +1733,11 @@
int oldState = connectionState;
connectionState = cs_ERROR;
+ try {
+ inputRecord.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
inboundDone = true;
sess.invalidate();
@@ -1728,14 +1765,15 @@
Alerts.getSSLException(description, cause, diagnostic);
}
- writer.closeOutbound();
+ try {
+ outputRecord.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
+ outboundDone = true;
connectionState = cs_CLOSED;
- // See comment in changeReadCiphers()
- readCipher.dispose();
- writeCipher.dispose();
-
if (cause instanceof RuntimeException) {
throw (RuntimeException)cause;
} else {
@@ -1747,9 +1785,10 @@
* Process an incoming alert ... caller must already have synchronized
* access to "this".
*/
- private void recvAlert() throws IOException {
- byte level = (byte)inputRecord.read();
- byte description = (byte)inputRecord.read();
+ private void recvAlert(ByteBuffer fragment) throws IOException {
+ byte level = fragment.get();
+ byte description = fragment.get();
+
if (description == -1) { // check for short message
fatal(Alerts.alert_illegal_parameter, "Short alert message");
}
@@ -1813,40 +1852,18 @@
// For initial handshaking, don't send alert message to peer if
// handshaker has not started.
- if (connectionState == cs_HANDSHAKE &&
- (handshaker == null || !handshaker.started())) {
+ //
+ // Shall we send an fatal alter to terminate the connection gracefully?
+ if (connectionState <= cs_HANDSHAKE &&
+ (handshaker == null || !handshaker.started() ||
+ !handshaker.activated())) {
return;
}
- EngineOutputRecord r = new EngineOutputRecord(Record.ct_alert, this);
- r.setVersion(protocolVersion);
-
- boolean useDebug = debug != null && Debug.isOn("ssl");
- if (useDebug) {
- synchronized (System.out) {
- System.out.print(Thread.currentThread().getName());
- System.out.print(", SEND " + protocolVersion + " ALERT: ");
- if (level == Alerts.alert_fatal) {
- System.out.print("fatal, ");
- } else if (level == Alerts.alert_warning) {
- System.out.print("warning, ");
- } else {
- System.out.print("<level = " + (0x0ff & level) + ">, ");
- }
- System.out.println("description = "
- + Alerts.alertDescription(description));
- }
- }
-
- r.write(level);
- r.write(description);
try {
- writeRecord(r);
- } catch (IOException e) {
- if (useDebug) {
- System.out.println(Thread.currentThread().getName() +
- ", Exception sending alert: " + e);
- }
+ outputRecord.encodeAlert(level, description);
+ } catch (IOException ioe) {
+ // ignore
}
}
@@ -1894,7 +1911,8 @@
@Override
synchronized public void setNeedClientAuth(boolean flag) {
doClientAuth = (flag ?
- SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none);
+ ClientAuthType.CLIENT_AUTH_REQUIRED :
+ ClientAuthType.CLIENT_AUTH_NONE);
if ((handshaker != null) &&
(handshaker instanceof ServerHandshaker) &&
@@ -1905,7 +1923,7 @@
@Override
synchronized public boolean getNeedClientAuth() {
- return (doClientAuth == SSLEngineImpl.clauth_required);
+ return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED);
}
/**
@@ -1919,7 +1937,8 @@
@Override
synchronized public void setWantClientAuth(boolean flag) {
doClientAuth = (flag ?
- SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none);
+ ClientAuthType.CLIENT_AUTH_REQUESTED :
+ ClientAuthType.CLIENT_AUTH_NONE);
if ((handshaker != null) &&
(handshaker instanceof ServerHandshaker) &&
@@ -1930,7 +1949,7 @@
@Override
synchronized public boolean getWantClientAuth() {
- return (doClientAuth == SSLEngineImpl.clauth_requested);
+ return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED);
}
@@ -1946,13 +1965,21 @@
case cs_START:
/*
- * If we need to change the engine mode and the enabled
- * protocols haven't specifically been set by the user,
- * change them to the corresponding default ones.
+ * If we need to change the socket mode and the enabled
+ * protocols and cipher suites haven't specifically been
+ * set by the user, change them to the corresponding
+ * default ones.
*/
- if (roleIsServer != (!flag) &&
- sslContext.isDefaultProtocolList(enabledProtocols)) {
- enabledProtocols = sslContext.getDefaultProtocolList(!flag);
+ if (roleIsServer != (!flag)) {
+ if (sslContext.isDefaultProtocolList(enabledProtocols)) {
+ enabledProtocols =
+ sslContext.getDefaultProtocolList(!flag);
+ }
+
+ if (sslContext.isDefaultCipherSuiteList(enabledCipherSuites)) {
+ enabledCipherSuites =
+ sslContext.getDefaultCipherSuiteList(!flag);
+ }
}
roleIsServer = !flag;
@@ -1970,13 +1997,22 @@
assert(handshaker != null);
if (!handshaker.activated()) {
/*
- * If we need to change the engine mode and the enabled
- * protocols haven't specifically been set by the user,
- * change them to the corresponding default ones.
+ * If we need to change the socket mode and the enabled
+ * protocols and cipher suites haven't specifically been
+ * set by the user, change them to the corresponding
+ * default ones.
*/
- if (roleIsServer != (!flag) &&
- sslContext.isDefaultProtocolList(enabledProtocols)) {
- enabledProtocols = sslContext.getDefaultProtocolList(!flag);
+ if (roleIsServer != (!flag)) {
+ if (sslContext.isDefaultProtocolList(enabledProtocols)) {
+ enabledProtocols =
+ sslContext.getDefaultProtocolList(!flag);
+ }
+
+ if (sslContext.isDefaultCipherSuiteList(
+ enabledCipherSuites)) {
+ enabledCipherSuites =
+ sslContext.getDefaultCipherSuiteList(!flag);
+ }
}
roleIsServer = !flag;
@@ -2102,6 +2138,8 @@
params.setSNIMatchers(sniMatchers);
params.setServerNames(serverNames);
params.setUseCipherSuitesOrder(preferLocalCipherSuites);
+ params.setEnableRetransmissions(enableRetransmissions);
+ params.setMaximumPacketSize(maximumPacketSize);
return params;
}
@@ -2117,6 +2155,15 @@
identificationProtocol = params.getEndpointIdentificationAlgorithm();
algorithmConstraints = params.getAlgorithmConstraints();
preferLocalCipherSuites = params.getUseCipherSuitesOrder();
+ enableRetransmissions = params.getEnableRetransmissions();
+ maximumPacketSize = params.getMaximumPacketSize();
+
+ if (maximumPacketSize != 0) {
+ outputRecord.changePacketSize(maximumPacketSize);
+ } else {
+ // use the implicit maximum packet size.
+ maximumPacketSize = outputRecord.getMaxPacketSize();
+ }
List<SNIServerName> sniNames = params.getServerNames();
if (sniNames != null) {
@@ -2131,6 +2178,7 @@
if ((handshaker != null) && !handshaker.started()) {
handshaker.setIdentificationProtocol(identificationProtocol);
handshaker.setAlgorithmConstraints(algorithmConstraints);
+ handshaker.setMaximumPacketSize(maximumPacketSize);
if (roleIsServer) {
handshaker.setSNIMatchers(sniMatchers);
handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites);
@@ -2141,14 +2189,6 @@
}
/**
- * Returns a boolean indicating whether the ChangeCipherSpec message
- * has been received for this handshake.
- */
- boolean receivedChangeCipherSpec() {
- return receivedCCS;
- }
-
- /**
* Returns a printable representation of this end of the connection.
*/
@Override
@@ -2162,6 +2202,7 @@
retval.append((host == null) ? "null" : host);
retval.append(" port=");
retval.append(Integer.toString(getPeerPort()));
+ retval.append(" role=" + (roleIsServer ? "Server" : "Client"));
retval.append("] ");
retval.append(getSession().getCipherSuite());
retval.append("]");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.*;
+import java.nio.*;
+
+import javax.crypto.BadPaddingException;
+
+import javax.net.ssl.*;
+
+import sun.misc.HexDumpEncoder;
+
+
+/**
+ * {@code InputRecord} implementation for {@code SSLEngine}.
+ */
+final class SSLEngineInputRecord extends InputRecord implements SSLRecord {
+ // used by handshake hash computation for handshake fragment
+ private byte prevType = -1;
+ private int hsMsgOff = 0;
+ private int hsMsgLen = 0;
+
+ private boolean formatVerified = false; // SSLv2 ruled out?
+
+ SSLEngineInputRecord() {
+ this.readAuthenticator = MAC.TLS_NULL;
+ }
+
+ @Override
+ int estimateFragmentSize(int packetSize) {
+ int macLen = 0;
+ if (readAuthenticator instanceof MAC) {
+ macLen = ((MAC)readAuthenticator).MAClen();
+ }
+
+ if (packetSize > 0) {
+ return readCipher.estimateFragmentSize(
+ packetSize, macLen, headerSize);
+ } else {
+ return Record.maxDataSize;
+ }
+ }
+
+ @Override
+ int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
+ /*
+ * SSLv2 length field is in bytes 0/1
+ * SSLv3/TLS length field is in bytes 3/4
+ */
+ if (packet.remaining() < 5) {
+ return -1;
+ }
+
+ int pos = packet.position();
+ byte byteZero = packet.get(pos);
+
+ int len = 0;
+
+ /*
+ * If we have already verified previous packets, we can
+ * ignore the verifications steps, and jump right to the
+ * determination. Otherwise, try one last hueristic to
+ * see if it's SSL/TLS.
+ */
+ if (formatVerified ||
+ (byteZero == ct_handshake) || (byteZero == ct_alert)) {
+ /*
+ * Last sanity check that it's not a wild record
+ */
+ ProtocolVersion recordVersion = ProtocolVersion.valueOf(
+ packet.get(pos + 1), packet.get(pos + 2));
+
+ // check the record version
+ checkRecordVersion(recordVersion, false);
+
+ /*
+ * Reasonably sure this is a V3, disable further checks.
+ * We can't do the same in the v2 check below, because
+ * read still needs to parse/handle the v2 clientHello.
+ */
+ formatVerified = true;
+
+ /*
+ * One of the SSLv3/TLS message types.
+ */
+ len = ((packet.get(pos + 3) & 0xFF) << 8) +
+ (packet.get(pos + 4) & 0xFF) + headerSize;
+
+ } else {
+ /*
+ * Must be SSLv2 or something unknown.
+ * Check if it's short (2 bytes) or
+ * long (3) header.
+ *
+ * Internals can warn about unsupported SSLv2
+ */
+ boolean isShort = ((byteZero & 0x80) != 0);
+
+ if (isShort &&
+ ((packet.get(pos + 2) == 1) || packet.get(pos + 2) == 4)) {
+
+ ProtocolVersion recordVersion = ProtocolVersion.valueOf(
+ packet.get(pos + 3), packet.get(pos + 4));
+
+ // check the record version
+ checkRecordVersion(recordVersion, true);
+
+ /*
+ * Client or Server Hello
+ */
+ int mask = (isShort ? 0x7F : 0x3F);
+ len = ((byteZero & mask) << 8) +
+ (packet.get(pos + 1) & 0xFF) + (isShort ? 2 : 3);
+
+ } else {
+ // Gobblygook!
+ throw new SSLException(
+ "Unrecognized SSL message, plaintext connection?");
+ }
+ }
+
+ return len;
+ }
+
+ @Override
+ void checkRecordVersion(ProtocolVersion recordVersion,
+ boolean allowSSL20Hello) throws SSLException {
+
+ if (recordVersion.maybeDTLSProtocol()) {
+ throw new SSLException(
+ "Unrecognized record version " + recordVersion +
+ " , DTLS packet?");
+ }
+
+ // Check if the record version is too old.
+ if ((recordVersion.v < ProtocolVersion.MIN.v)) {
+ // if it's not SSLv2, we're out of here.
+ if (!allowSSL20Hello ||
+ (recordVersion.v != ProtocolVersion.SSL20Hello.v)) {
+ throw new SSLException(
+ "Unsupported record version " + recordVersion);
+ }
+ }
+ }
+
+ @Override
+ Plaintext decode(ByteBuffer packet)
+ throws IOException, BadPaddingException {
+
+ if (isClosed) {
+ return null;
+ }
+
+ if (debug != null && Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Raw read]: length = " + packet.remaining(), packet);
+ }
+
+ // The caller should have validated the record.
+ if (!formatVerified) {
+ formatVerified = true;
+
+ /*
+ * The first record must either be a handshake record or an
+ * alert message. If it's not, it is either invalid or an
+ * SSLv2 message.
+ */
+ int pos = packet.position();
+ byte byteZero = packet.get(pos);
+ if (byteZero != ct_handshake && byteZero != ct_alert) {
+ return handleUnknownRecord(packet);
+ }
+ }
+
+ return decodeInputRecord(packet);
+ }
+
+ private Plaintext decodeInputRecord(ByteBuffer packet)
+ throws IOException, BadPaddingException {
+
+ //
+ // The packet should be a complete record, or more.
+ //
+
+ int srcPos = packet.position();
+ int srcLim = packet.limit();
+
+ byte contentType = packet.get(); // pos: 0
+ byte majorVersion = packet.get(); // pos: 1
+ byte minorVersion = packet.get(); // pos: 2
+ int contentLen = ((packet.get() & 0xFF) << 8) +
+ (packet.get() & 0xFF); // pos: 3, 4
+
+ if (debug != null && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", READ: " +
+ ProtocolVersion.valueOf(majorVersion, minorVersion) +
+ " " + Record.contentName(contentType) + ", length = " +
+ contentLen);
+ }
+
+ //
+ // Check for upper bound.
+ //
+ // Note: May check packetSize limit in the future.
+ if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {
+ throw new SSLProtocolException(
+ "Bad input record size, TLSCiphertext.length = " + contentLen);
+ }
+
+ //
+ // check for handshake fragment
+ //
+ if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) {
+ throw new SSLProtocolException(
+ "Expected to get a handshake fragment");
+ }
+
+ //
+ // Decrypt the fragment
+ //
+ int recLim = srcPos + SSLRecord.headerSize + contentLen;
+ packet.limit(recLim);
+ packet.position(srcPos + SSLRecord.headerSize);
+
+ ByteBuffer plaintext;
+ try {
+ plaintext =
+ decrypt(readAuthenticator, readCipher, contentType, packet);
+ } finally {
+ // comsume a complete record
+ packet.limit(srcLim);
+ packet.position(recLim);
+ }
+
+ //
+ // handshake hashing
+ //
+ if (contentType == ct_handshake) {
+ int pltPos = plaintext.position();
+ int pltLim = plaintext.limit();
+ int frgPos = pltPos;
+ for (int remains = plaintext.remaining(); remains > 0;) {
+ int howmuch;
+ byte handshakeType;
+ if (hsMsgOff < hsMsgLen) {
+ // a fragment of the handshake message
+ howmuch = Math.min((hsMsgLen - hsMsgOff), remains);
+ handshakeType = prevType;
+
+ hsMsgOff += howmuch;
+ if (hsMsgOff == hsMsgLen) {
+ // Now is a complete handshake message.
+ hsMsgOff = 0;
+ hsMsgLen = 0;
+ }
+ } else { // hsMsgOff == hsMsgLen, a new handshake message
+ handshakeType = plaintext.get();
+ int handshakeLen = ((plaintext.get() & 0xFF) << 16) |
+ ((plaintext.get() & 0xFF) << 8) |
+ (plaintext.get() & 0xFF);
+ plaintext.position(frgPos);
+ if (remains < (handshakeLen + 1)) { // 1: handshake type
+ // This handshake message is fragmented.
+ prevType = handshakeType;
+ hsMsgOff = remains - 4; // 4: handshake header
+ hsMsgLen = handshakeLen;
+ }
+
+ howmuch = Math.min(handshakeLen + 4, remains);
+ }
+
+ plaintext.limit(frgPos + howmuch);
+
+ if (handshakeType == HandshakeMessage.ht_hello_request) {
+ // omitted from handshake hash computation
+ } else if ((handshakeType != HandshakeMessage.ht_finished) &&
+ (handshakeType != HandshakeMessage.ht_certificate_verify)) {
+
+ if (handshakeHash == null) {
+ // used for cache only
+ handshakeHash = new HandshakeHash(false);
+ }
+ handshakeHash.update(plaintext);
+ } else {
+ // Reserve until this handshake message has been processed.
+ if (handshakeHash == null) {
+ // used for cache only
+ handshakeHash = new HandshakeHash(false);
+ }
+ handshakeHash.reserve(plaintext);
+ }
+
+ plaintext.position(frgPos + howmuch);
+ plaintext.limit(pltLim);
+
+ frgPos += howmuch;
+ remains -= howmuch;
+ }
+
+ plaintext.position(pltPos);
+ }
+
+ return new Plaintext(contentType,
+ majorVersion, minorVersion, -1, -1L, plaintext);
+ // recordEpoch, recordSeq, plaintext);
+ }
+
+ private Plaintext handleUnknownRecord(ByteBuffer packet)
+ throws IOException, BadPaddingException {
+
+ //
+ // The packet should be a complete record.
+ //
+ int srcPos = packet.position();
+ int srcLim = packet.limit();
+
+ byte firstByte = packet.get(srcPos);
+ byte thirdByte = packet.get(srcPos + 2);
+
+ // Does it look like a Version 2 client hello (V2ClientHello)?
+ if (((firstByte & 0x80) != 0) && (thirdByte == 1)) {
+ /*
+ * If SSLv2Hello is not enabled, throw an exception.
+ */
+ if (helloVersion != ProtocolVersion.SSL20Hello) {
+ throw new SSLHandshakeException("SSLv2Hello is not enabled");
+ }
+
+ byte majorVersion = packet.get(srcPos + 3);
+ byte minorVersion = packet.get(srcPos + 4);
+
+ if ((majorVersion == ProtocolVersion.SSL20Hello.major) &&
+ (minorVersion == ProtocolVersion.SSL20Hello.minor)) {
+
+ /*
+ * Looks like a V2 client hello, but not one saying
+ * "let's talk SSLv3". So we need to send an SSLv2
+ * error message, one that's treated as fatal by
+ * clients (Otherwise we'll hang.)
+ */
+ if (debug != null && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ "Requested to negotiate unsupported SSLv2!");
+ }
+
+ // hack code, the exception is caught in SSLEngineImpl
+ // so that SSLv2 error message can be delivered properly.
+ throw new UnsupportedOperationException( // SSLv2Hello
+ "Unsupported SSL v2.0 ClientHello");
+ }
+
+ /*
+ * If we can map this into a V3 ClientHello, read and
+ * hash the rest of the V2 handshake, turn it into a
+ * V3 ClientHello message, and pass it up.
+ */
+ packet.position(srcPos + 2); // exclude the header
+
+ if (handshakeHash == null) {
+ // used for cache only
+ handshakeHash = new HandshakeHash(false);
+ }
+ handshakeHash.update(packet);
+ packet.position(srcPos);
+
+ ByteBuffer converted = convertToClientHello(packet);
+
+ if (debug != null && Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Converted] ClientHello", converted);
+ }
+
+ return new Plaintext(ct_handshake,
+ majorVersion, minorVersion, -1, -1L, converted);
+ } else {
+ if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
+ throw new SSLException("SSL V2.0 servers are not supported.");
+ }
+
+ throw new SSLException("Unsupported or unrecognized SSL message");
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,570 @@
+/*
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import sun.misc.HexDumpEncoder;
+import static sun.security.ssl.Ciphertext.RecordType;
+
+/**
+ * {@code OutputRecord} implementation for {@code SSLEngine}.
+ */
+final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord {
+
+ private HandshakeFragment fragmenter = null;
+ private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
+ private boolean isTalkingToV2 = false; // SSLv2Hello
+ private ByteBuffer v2ClientHello = null; // SSLv2Hello
+
+ private boolean isCloseWaiting = false;
+
+ SSLEngineOutputRecord() {
+ this.writeAuthenticator = MAC.TLS_NULL;
+
+ this.packetSize = SSLRecord.maxRecordSize;
+ this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
+ }
+
+ @Override
+ synchronized public void close() throws IOException {
+ if (!isClosed) {
+ if (alertMemos != null && !alertMemos.isEmpty()) {
+ isCloseWaiting = true;
+ } else {
+ super.close();
+ }
+ }
+ }
+
+ @Override
+ void encodeAlert(byte level, byte description) throws IOException {
+ RecordMemo memo = new RecordMemo();
+
+ memo.contentType = Record.ct_alert;
+ memo.majorVersion = protocolVersion.major;
+ memo.minorVersion = protocolVersion.minor;
+ memo.encodeCipher = writeCipher;
+ memo.encodeAuthenticator = writeAuthenticator;
+
+ memo.fragment = new byte[2];
+ memo.fragment[0] = level;
+ memo.fragment[1] = description;
+
+ alertMemos.add(memo);
+ }
+
+ @Override
+ void encodeHandshake(byte[] source,
+ int offset, int length) throws IOException {
+
+ if (fragmenter == null) {
+ fragmenter = new HandshakeFragment();
+ }
+
+ if (firstMessage) {
+ firstMessage = false;
+
+ if ((helloVersion == ProtocolVersion.SSL20Hello) &&
+ (source[offset] == HandshakeMessage.ht_client_hello) &&
+ // 5: recode header size
+ (source[offset + 4 + 2 + 32] == 0)) {
+ // V3 session ID is empty
+ // 4: handshake header size
+ // 2: client_version in ClientHello
+ // 32: random in ClientHello
+
+ // Double space should be big enough for the converted message.
+ v2ClientHello = encodeV2ClientHello(
+ source, (offset + 4), (length - 4));
+
+ v2ClientHello.position(2); // exclude the header
+ handshakeHash.update(v2ClientHello);
+ v2ClientHello.position(0);
+
+ return;
+ }
+ }
+
+ byte handshakeType = source[offset];
+ if (handshakeType != HandshakeMessage.ht_hello_request) {
+ handshakeHash.update(source, offset, length);
+ }
+
+ fragmenter.queueUpFragment(source, offset, length);
+ }
+
+ @Override
+ void encodeChangeCipherSpec() throws IOException {
+ if (fragmenter == null) {
+ fragmenter = new HandshakeFragment();
+ }
+ fragmenter.queueUpChangeCipherSpec();
+ }
+
+ @Override
+ void encodeV2NoCipher() throws IOException {
+ isTalkingToV2 = true;
+ }
+
+ @Override
+ Ciphertext encode(ByteBuffer[] sources, int offset, int length,
+ ByteBuffer destination) throws IOException {
+
+ if (writeAuthenticator.seqNumOverflow()) {
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", sequence number extremely close to overflow " +
+ "(2^64-1 packets). Closing connection.");
+ }
+
+ throw new SSLHandshakeException("sequence number overflow");
+ }
+
+ int macLen = 0;
+ if (writeAuthenticator instanceof MAC) {
+ macLen = ((MAC)writeAuthenticator).MAClen();
+ }
+
+ int dstLim = destination.limit();
+ boolean isFirstRecordOfThePayload = true;
+ int packetLeftSize = Math.min(maxRecordSize, packetSize);
+ boolean needMorePayload = true;
+ long recordSN = 0L;
+ while (needMorePayload) {
+ int fragLen;
+ if (isFirstRecordOfThePayload && needToSplitPayload()) {
+ needMorePayload = true;
+
+ fragLen = 1;
+ isFirstRecordOfThePayload = false;
+ } else {
+ needMorePayload = false;
+
+ if (packetLeftSize > 0) {
+ fragLen = writeCipher.calculateFragmentSize(
+ packetLeftSize, macLen, headerSize);
+
+ fragLen = Math.min(fragLen, Record.maxDataSize);
+ } else {
+ fragLen = Record.maxDataSize;
+ }
+
+ if (fragmentSize > 0) {
+ fragLen = Math.min(fragLen, fragmentSize);
+ }
+ }
+
+ int dstPos = destination.position();
+ int dstContent = dstPos + headerSize +
+ writeCipher.getExplicitNonceSize();
+ destination.position(dstContent);
+
+ int remains = Math.min(fragLen, destination.remaining());
+ fragLen = 0;
+ int srcsLen = offset + length;
+ for (int i = offset; (i < srcsLen) && (remains > 0); i++) {
+ int amount = Math.min(sources[i].remaining(), remains);
+ int srcLimit = sources[i].limit();
+ sources[i].limit(sources[i].position() + amount);
+ destination.put(sources[i]);
+ sources[i].limit(srcLimit); // restore the limit
+ remains -= amount;
+ fragLen += amount;
+
+ if (remains > 0) {
+ offset++;
+ length--;
+ }
+ }
+
+ destination.limit(destination.position());
+ destination.position(dstContent);
+
+ if ((debug != null) && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", WRITE: " + protocolVersion + " " +
+ Record.contentName(Record.ct_application_data) +
+ ", length = " + destination.remaining());
+ }
+
+ // Encrypt the fragment and wrap up a record.
+ recordSN = encrypt(writeAuthenticator, writeCipher,
+ Record.ct_application_data, destination,
+ dstPos, dstLim, headerSize,
+ protocolVersion, false);
+
+ if ((debug != null) && Debug.isOn("packet")) {
+ ByteBuffer temporary = destination.duplicate();
+ temporary.limit(temporary.position());
+ temporary.position(dstPos);
+ Debug.printHex(
+ "[Raw write]: length = " + temporary.remaining(),
+ temporary);
+ }
+
+ packetLeftSize -= destination.position() - dstPos;
+
+ // remain the limit unchanged
+ destination.limit(dstLim);
+
+ if (isFirstAppOutputRecord) {
+ isFirstAppOutputRecord = false;
+ }
+ }
+
+ return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
+ }
+
+ @Override
+ Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
+ if (isTalkingToV2) { // SSLv2Hello
+ // We don't support SSLv2. Send an SSLv2 error message
+ // so that the connection can be closed gracefully.
+ //
+ // Please don't change the limit of the destination buffer.
+ destination.put(SSLRecord.v2NoCipher);
+ if (debug != null && Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Raw write]: length = " + SSLRecord.v2NoCipher.length,
+ SSLRecord.v2NoCipher);
+ }
+
+ isTalkingToV2 = false;
+
+ return new Ciphertext(RecordType.RECORD_ALERT, -1L);
+ }
+
+ if (v2ClientHello != null) {
+ // deliver the SSLv2 format ClientHello message
+ //
+ // Please don't change the limit of the destination buffer.
+ if (debug != null) {
+ if (Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", WRITE: SSLv2 ClientHello message" +
+ ", length = " + v2ClientHello.remaining());
+ }
+
+ if (Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Raw write]: length = " + v2ClientHello.remaining(),
+ v2ClientHello);
+ }
+ }
+
+ destination.put(v2ClientHello);
+ v2ClientHello = null;
+
+ return new Ciphertext(RecordType.RECORD_CLIENT_HELLO, -1L);
+ }
+
+ if (alertMemos != null && !alertMemos.isEmpty()) {
+ RecordMemo memo = alertMemos.pop();
+
+ int macLen = 0;
+ if (memo.encodeAuthenticator instanceof MAC) {
+ macLen = ((MAC)memo.encodeAuthenticator).MAClen();
+ }
+
+ int dstPos = destination.position();
+ int dstLim = destination.limit();
+ int dstContent = dstPos + headerSize +
+ writeCipher.getExplicitNonceSize();
+ destination.position(dstContent);
+
+ destination.put(memo.fragment);
+
+ destination.limit(destination.position());
+ destination.position(dstContent);
+
+ if ((debug != null) && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", WRITE: " + protocolVersion + " " +
+ Record.contentName(Record.ct_alert) +
+ ", length = " + destination.remaining());
+ }
+
+ // Encrypt the fragment and wrap up a record.
+ long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
+ Record.ct_alert, destination, dstPos, dstLim, headerSize,
+ ProtocolVersion.valueOf(memo.majorVersion,
+ memo.minorVersion), false);
+
+ if ((debug != null) && Debug.isOn("packet")) {
+ ByteBuffer temporary = destination.duplicate();
+ temporary.limit(temporary.position());
+ temporary.position(dstPos);
+ Debug.printHex(
+ "[Raw write]: length = " + temporary.remaining(),
+ temporary);
+ }
+
+ // remain the limit unchanged
+ destination.limit(dstLim);
+
+ if (isCloseWaiting && (memo.contentType == Record.ct_alert)) {
+ isCloseWaiting = true;
+ close();
+ }
+ return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
+ }
+
+ if (fragmenter != null) {
+ return fragmenter.acquireCiphertext(destination);
+ }
+
+ return null;
+ }
+
+ @Override
+ boolean isEmpty() {
+ return (!isTalkingToV2) && (v2ClientHello == null) &&
+ ((fragmenter == null) || fragmenter.isEmpty()) &&
+ ((alertMemos == null) || alertMemos.isEmpty());
+ }
+
+ // buffered record fragment
+ private static class RecordMemo {
+ byte contentType;
+ byte majorVersion;
+ byte minorVersion;
+ CipherBox encodeCipher;
+ Authenticator encodeAuthenticator;
+
+ byte[] fragment;
+ }
+
+ private static class HandshakeMemo extends RecordMemo {
+ byte handshakeType;
+ int acquireOffset;
+ }
+
+ final class HandshakeFragment {
+ private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
+
+ void queueUpFragment(byte[] source,
+ int offset, int length) throws IOException {
+
+ HandshakeMemo memo = new HandshakeMemo();
+
+ memo.contentType = Record.ct_handshake;
+ memo.majorVersion = protocolVersion.major; // kick start version?
+ memo.minorVersion = protocolVersion.minor;
+ memo.encodeCipher = writeCipher;
+ memo.encodeAuthenticator = writeAuthenticator;
+
+ memo.handshakeType = source[offset];
+ memo.acquireOffset = 0;
+ memo.fragment = new byte[length - 4]; // 4: header size
+ // 1: HandshakeType
+ // 3: message length
+ System.arraycopy(source, offset + 4, memo.fragment, 0, length - 4);
+
+ handshakeMemos.add(memo);
+ }
+
+ void queueUpChangeCipherSpec() {
+ RecordMemo memo = new RecordMemo();
+
+ memo.contentType = Record.ct_change_cipher_spec;
+ memo.majorVersion = protocolVersion.major;
+ memo.minorVersion = protocolVersion.minor;
+ memo.encodeCipher = writeCipher;
+ memo.encodeAuthenticator = writeAuthenticator;
+
+ memo.fragment = new byte[1];
+ memo.fragment[0] = 1;
+
+ handshakeMemos.add(memo);
+ }
+
+ Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
+ if (isEmpty()) {
+ return null;
+ }
+
+ RecordMemo memo = handshakeMemos.getFirst();
+ HandshakeMemo hsMemo = null;
+ if (memo.contentType == Record.ct_handshake) {
+ hsMemo = (HandshakeMemo)memo;
+ }
+
+ int macLen = 0;
+ if (memo.encodeAuthenticator instanceof MAC) {
+ macLen = ((MAC)memo.encodeAuthenticator).MAClen();
+ }
+
+ // ChangeCipherSpec message is pretty small. Don't worry about
+ // the fragmentation of ChangeCipherSpec record.
+ int fragLen;
+ if (packetSize > 0) {
+ fragLen = Math.min(maxRecordSize, packetSize);
+ fragLen = memo.encodeCipher.calculateFragmentSize(
+ fragLen, macLen, headerSize);
+ } else {
+ fragLen = Record.maxDataSize;
+ }
+
+ if (fragmentSize > 0) {
+ fragLen = Math.min(fragLen, fragmentSize);
+ }
+
+ int dstPos = dstBuf.position();
+ int dstLim = dstBuf.limit();
+ int dstContent = dstPos + headerSize +
+ memo.encodeCipher.getExplicitNonceSize();
+ dstBuf.position(dstContent);
+
+ if (hsMemo != null) {
+ int remainingFragLen = fragLen;
+ while ((remainingFragLen > 0) && !handshakeMemos.isEmpty()) {
+ int memoFragLen = hsMemo.fragment.length;
+ if (hsMemo.acquireOffset == 0) {
+ // Don't fragment handshake message header
+ if (remainingFragLen <= 4) {
+ break;
+ }
+
+ dstBuf.put(hsMemo.handshakeType);
+ dstBuf.put((byte)((memoFragLen >> 16) & 0xFF));
+ dstBuf.put((byte)((memoFragLen >> 8) & 0xFF));
+ dstBuf.put((byte)(memoFragLen & 0xFF));
+
+ remainingFragLen -= 4;
+ } // Otherwise, handshake message is fragmented.
+
+ int chipLen = Math.min(remainingFragLen,
+ (memoFragLen - hsMemo.acquireOffset));
+ dstBuf.put(hsMemo.fragment, hsMemo.acquireOffset, chipLen);
+
+ hsMemo.acquireOffset += chipLen;
+ if (hsMemo.acquireOffset == memoFragLen) {
+ handshakeMemos.removeFirst();
+
+ // still have space for more records?
+ if ((remainingFragLen > chipLen) &&
+ !handshakeMemos.isEmpty()) {
+
+ // look for the next buffered record fragment
+ RecordMemo reMemo = handshakeMemos.getFirst();
+ if (reMemo.contentType == Record.ct_handshake) {
+ hsMemo = (HandshakeMemo)reMemo;
+ } else {
+ // not handshake message, break the loop
+ break;
+ }
+ }
+ }
+
+ remainingFragLen -= chipLen;
+ }
+
+ fragLen -= remainingFragLen;
+ } else {
+ fragLen = Math.min(fragLen, memo.fragment.length);
+ dstBuf.put(memo.fragment, 0, fragLen);
+
+ handshakeMemos.removeFirst();
+ }
+
+ dstBuf.limit(dstBuf.position());
+ dstBuf.position(dstContent);
+
+ if ((debug != null) && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", WRITE: " + protocolVersion + " " +
+ Record.contentName(memo.contentType) +
+ ", length = " + dstBuf.remaining());
+ }
+
+ // Encrypt the fragment and wrap up a record.
+ long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
+ memo.contentType, dstBuf,
+ dstPos, dstLim, headerSize,
+ ProtocolVersion.valueOf(memo.majorVersion,
+ memo.minorVersion), false);
+
+ if ((debug != null) && Debug.isOn("packet")) {
+ ByteBuffer temporary = dstBuf.duplicate();
+ temporary.limit(temporary.position());
+ temporary.position(dstPos);
+ Debug.printHex(
+ "[Raw write]: length = " + temporary.remaining(),
+ temporary);
+ }
+
+ // remain the limit unchanged
+ dstBuf.limit(dstLim);
+
+ // Reset the fragmentation offset.
+ if (hsMemo != null) {
+ return new Ciphertext(RecordType.valueOf(
+ hsMemo.contentType, hsMemo.handshakeType), recordSN);
+ } else {
+ return new Ciphertext(
+ RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
+ }
+ }
+
+ boolean isEmpty() {
+ return handshakeMemos.isEmpty();
+ }
+ }
+
+ /*
+ * Need to split the payload except the following cases:
+ *
+ * 1. protocol version is TLS 1.1 or later;
+ * 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
+ * 3. the payload is the first application record of a freshly
+ * negotiated TLS session.
+ * 4. the CBC protection is disabled;
+ *
+ * By default, we counter chosen plaintext issues on CBC mode
+ * ciphersuites in SSLv3/TLS1.0 by sending one byte of application
+ * data in the first record of every payload, and the rest in
+ * subsequent record(s). Note that the issues have been solved in
+ * TLS 1.1 or later.
+ *
+ * It is not necessary to split the very first application record of
+ * a freshly negotiated TLS session, as there is no previous
+ * application data to guess. To improve compatibility, we will not
+ * split such records.
+ *
+ * This avoids issues in the outbound direction. For a full fix,
+ * the peer must have similar protections.
+ */
+ boolean needToSplitPayload() {
+ return (!protocolVersion.useTLS11PlusSpec()) &&
+ writeCipher.isCBCMode() && !isFirstAppOutputRecord &&
+ Record.enableCBCProtection;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLRecord.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/**
+ * SSL/TLS record
+ *
+ * @author David Brownell
+ */
+interface SSLRecord extends Record {
+
+ static final int headerSize = 5; // SSLv3 record header
+
+ /*
+ * The size of the header plus the max IV length
+ */
+ static final int headerPlusMaxIVSize =
+ headerSize // header
+ + maxIVLength; // iv
+
+ /*
+ * The maximum size that may be increased when translating plaintext to
+ * ciphertext fragment.
+ */
+ static final int maxPlaintextPlusSize =
+ headerSize // header
+ + maxIVLength // iv
+ + maxMacSize // MAC or AEAD tag
+ + maxPadding; // block cipher padding
+
+ /*
+ * SSL has a maximum record size. It's header, (compressed) data,
+ * padding, and a trailer for the message authentication information (MAC
+ * for block and stream ciphers, and message authentication tag for AEAD
+ * ciphers).
+ *
+ * Some compression algorithms have rare cases where they expand the data.
+ * As we don't support compression at this time, leave that out.
+ */
+ static final int maxRecordSize =
+ headerPlusMaxIVSize // header + iv
+ + maxDataSize // data
+ + maxPadding // padding
+ + maxMacSize; // MAC or AEAD tag
+
+ /*
+ * For CBC protection in SSL3/TLS1, we break some plaintext into two
+ * packets. Max application data size for the second packet.
+ */
+ static final int maxDataSizeMinusOneByteRecord =
+ maxDataSize // max data size
+ - ( // max one byte record size
+ headerPlusMaxIVSize // header + iv
+ + 1 // one byte data
+ + maxPadding // padding
+ + maxMacSize // MAC
+ );
+
+ /*
+ * The maximum large record size.
+ *
+ * Some SSL/TLS implementations support large fragment upto 2^15 bytes,
+ * such as Microsoft. We support large incoming fragments.
+ *
+ * The maximum large record size is defined as maxRecordSize plus 2^14,
+ * this is the amount OpenSSL is using.
+ */
+ static final int maxLargeRecordSize =
+ maxRecordSize // Max size with a conforming implementation
+ + maxDataSize; // extra 2^14 bytes for large data packets.
+
+
+ /*
+ * Maximum record size for alert and change cipher spec records.
+ * They only contain 2 and 1 bytes of data, respectively.
+ * Allocate a smaller array.
+ */
+ static final int maxAlertRecordSize =
+ headerPlusMaxIVSize // header + iv
+ + 2 // alert
+ + maxPadding // padding
+ + maxMacSize; // MAC
+
+ /*
+ * We may need to send this SSL v2 "No Cipher" message back, if we
+ * are faced with an SSLv2 "hello" that's not saying "I talk v3".
+ * It's the only one documented in the V2 spec as a fatal error.
+ */
+ static final byte[] v2NoCipher = {
+ (byte)0x80, (byte)0x03, // unpadded 3 byte record
+ (byte)0x00, // ... error message
+ (byte)0x00, (byte)0x01 // ... NO_CIPHER error
+ };
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLServerSocketImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLServerSocketImpl.java Thu Jun 04 18:49:37 2015 -0700
@@ -68,7 +68,7 @@
private SSLContextImpl sslContext;
/* Do newly accepted connections require clients to authenticate? */
- private byte doClientAuth = SSLEngineImpl.clauth_none;
+ private ClientAuthType clientAuthType = ClientAuthType.CLIENT_AUTH_NONE;
/* Do new connections created here use the "server" mode of SSL? */
private boolean useServerMode = true;
@@ -230,13 +230,13 @@
*/
@Override
public void setNeedClientAuth(boolean flag) {
- doClientAuth = (flag ?
- SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none);
+ clientAuthType = (flag ? ClientAuthType.CLIENT_AUTH_REQUIRED :
+ ClientAuthType.CLIENT_AUTH_NONE);
}
@Override
public boolean getNeedClientAuth() {
- return (doClientAuth == SSLEngineImpl.clauth_required);
+ return (clientAuthType == ClientAuthType.CLIENT_AUTH_REQUIRED);
}
/**
@@ -245,13 +245,13 @@
*/
@Override
public void setWantClientAuth(boolean flag) {
- doClientAuth = (flag ?
- SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none);
+ clientAuthType = (flag ? ClientAuthType.CLIENT_AUTH_REQUESTED :
+ ClientAuthType.CLIENT_AUTH_NONE);
}
@Override
public boolean getWantClientAuth() {
- return (doClientAuth == SSLEngineImpl.clauth_requested);
+ return (clientAuthType == ClientAuthType.CLIENT_AUTH_REQUESTED);
}
/**
@@ -341,7 +341,7 @@
@Override
public Socket accept() throws IOException {
SSLSocketImpl s = new SSLSocketImpl(sslContext, useServerMode,
- enabledCipherSuites, doClientAuth, enableSessionCreation,
+ enabledCipherSuites, clientAuthType, enableSessionCreation,
enabledProtocols, identificationProtocol, algorithmConstraints,
sniMatchers, preferLocalCipherSuites);
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java Thu Jun 04 18:49:37 2015 -0700
@@ -109,6 +109,8 @@
private String[] peerSupportedSignAlgs;
private List<SNIServerName> requestedServerNames;
+ private int negotiatedMaxFragLen;
+ private int maximumPacketSize;
// Principals for non-certificate based cipher suites
private Principal peerPrincipal;
@@ -177,6 +179,7 @@
sessionCount = ++counter;
localSupportedSignAlgs =
SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);
+ negotiatedMaxFragLen = -1;
if (debug != null && Debug.isOn("session")) {
System.out.println("%% Initialized: " + this);
@@ -422,10 +425,9 @@
// change record of peer identity even by accident, much
// less do it intentionally.
//
- if ((cipherSuite.keyExchange == K_KRB5) ||
- (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+ if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
throw new SSLPeerUnverifiedException("no certificates expected"
- + " for Kerberos cipher suites");
+ + " for " + cipherSuite.keyExchange + " cipher suites");
}
if (peerCerts == null) {
throw new SSLPeerUnverifiedException("peer not authenticated");
@@ -478,10 +480,9 @@
// change record of peer identity even by accident, much
// less do it intentionally.
//
- if ((cipherSuite.keyExchange == K_KRB5) ||
- (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+ if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
throw new SSLPeerUnverifiedException("no certificates expected"
- + " for Kerberos cipher suites");
+ + " for " + cipherSuite.keyExchange + " cipher suites");
}
if (peerCerts == null) {
throw new SSLPeerUnverifiedException("peer not authenticated");
@@ -519,10 +520,9 @@
* change record of peer identity even by accident, much
* less do it intentionally.
*/
- if ((cipherSuite.keyExchange == K_KRB5) ||
- (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+ if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
throw new SSLPeerUnverifiedException("no certificates expected"
- + " for Kerberos cipher suites");
+ + " for " + cipherSuite.keyExchange + " cipher suites");
}
if (peerCerts != null) {
return peerCerts.clone();
@@ -537,7 +537,7 @@
*
* @return the peer's principal. Returns an X500Principal of the
* end-entity certificate for X509-based cipher suites, and
- * Principal for Kerberos cipher suites.
+ * Principal for Kerberos cipher suites, etc.
*
* @throws SSLPeerUnverifiedException if the peer's identity has not
* been verified
@@ -546,12 +546,10 @@
public Principal getPeerPrincipal()
throws SSLPeerUnverifiedException
{
- if ((cipherSuite.keyExchange == K_KRB5) ||
- (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+ if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
if (peerPrincipal == null) {
throw new SSLPeerUnverifiedException("peer not authenticated");
} else {
- // Eliminate dependency on KerberosPrincipal
return peerPrincipal;
}
}
@@ -566,15 +564,13 @@
*
* @return the principal sent to the peer. Returns an X500Principal
* of the end-entity certificate for X509-based cipher suites, and
- * Principal for Kerberos cipher suites. If no principal was
+ * Principal for Kerberos cipher suites, etc. If no principal was
* sent, then null is returned.
*/
@Override
public Principal getLocalPrincipal() {
- if ((cipherSuite.keyExchange == K_KRB5) ||
- (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
- // Eliminate dependency on KerberosPrincipal
+ if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
return (localPrincipal == null ? null : localPrincipal);
}
return (localCerts == null ? null :
@@ -785,8 +781,30 @@
*/
@Override
public synchronized int getPacketBufferSize() {
- return acceptLargeFragments ?
- Record.maxLargeRecordSize : Record.maxRecordSize;
+ // Use the bigger packet size calculated from maximumPacketSize
+ // and negotiatedMaxFragLen.
+ int packetSize = 0;
+ if (negotiatedMaxFragLen > 0) {
+ packetSize = cipherSuite.calculatePacketSize(
+ negotiatedMaxFragLen, protocolVersion,
+ protocolVersion.isDTLSProtocol());
+ }
+
+ if (maximumPacketSize > 0) {
+ return (maximumPacketSize > packetSize) ?
+ maximumPacketSize : packetSize;
+ }
+
+ if (packetSize != 0) {
+ return packetSize;
+ }
+
+ if (protocolVersion.isDTLSProtocol()) {
+ return DTLSRecord.maxRecordSize;
+ } else {
+ return acceptLargeFragments ?
+ SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize;
+ }
}
/**
@@ -795,7 +813,64 @@
*/
@Override
public synchronized int getApplicationBufferSize() {
- return getPacketBufferSize() - Record.headerSize;
+ // Use the bigger fragment size calculated from maximumPacketSize
+ // and negotiatedMaxFragLen.
+ int fragmentSize = 0;
+ if (maximumPacketSize > 0) {
+ fragmentSize = cipherSuite.calculateFragSize(
+ maximumPacketSize, protocolVersion,
+ protocolVersion.isDTLSProtocol());
+ }
+
+ if (negotiatedMaxFragLen > 0) {
+ return (negotiatedMaxFragLen > fragmentSize) ?
+ negotiatedMaxFragLen : fragmentSize;
+ }
+
+ if (fragmentSize != 0) {
+ return fragmentSize;
+ }
+
+ if (protocolVersion.isDTLSProtocol()) {
+ return Record.maxDataSize;
+ } else {
+ int maxPacketSize = acceptLargeFragments ?
+ SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize;
+ return (maxPacketSize - SSLRecord.headerSize);
+ }
+ }
+
+ /**
+ * Sets the negotiated maximum fragment length, as specified by the
+ * max_fragment_length ClientHello extension in RFC 6066.
+ *
+ * @param negotiatedMaxFragLen
+ * the negotiated maximum fragment length, or {@code -1} if
+ * no such length has been negotiated.
+ */
+ synchronized void setNegotiatedMaxFragSize(
+ int negotiatedMaxFragLen) {
+
+ this.negotiatedMaxFragLen = negotiatedMaxFragLen;
+ }
+
+ /**
+ * Get the negotiated maximum fragment length, as specified by the
+ * max_fragment_length ClientHello extension in RFC 6066.
+ *
+ * @return the negotiated maximum fragment length, or {@code -1} if
+ * no such length has been negotiated.
+ */
+ synchronized int getNegotiatedMaxFragSize() {
+ return negotiatedMaxFragLen;
+ }
+
+ synchronized void setMaximumPacketSize(int maximumPacketSize) {
+ this.maximumPacketSize = maximumPacketSize;
+ }
+
+ synchronized int getMaximumPacketSize() {
+ return maximumPacketSize;
}
/**
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java Thu Jun 04 18:49:37 2015 -0700
@@ -27,6 +27,7 @@
package sun.security.ssl;
import java.io.*;
+import java.nio.*;
import java.net.*;
import java.security.GeneralSecurityException;
import java.security.AccessController;
@@ -155,30 +156,16 @@
private static final int cs_DATA = 2;
private static final int cs_RENEGOTIATE = 3;
private static final int cs_ERROR = 4;
- private static final int cs_SENT_CLOSE = 5;
+ private static final int cs_SENT_CLOSE = 5;
private static final int cs_CLOSED = 6;
private static final int cs_APP_CLOSED = 7;
-
- /*
- * Client authentication be off, requested, or required.
- *
- * Migrated to SSLEngineImpl:
- * clauth_none/cl_auth_requested/clauth_required
- */
-
/*
* Drives the protocol state machine.
*/
private volatile int connectionState;
/*
- * Flag indicating that the engine's handshaker has done the necessary
- * steps so the engine may process a ChangeCipherSpec message.
- */
- private boolean receivedCCS;
-
- /*
* Flag indicating if the next record we receive MUST be a Finished
* message. Temporarily set during the handshake to ensure that
* a change cipher spec message is followed by a finished message.
@@ -197,7 +184,8 @@
* Per-connection private state that doesn't change when the
* session is changed.
*/
- private byte doClientAuth;
+ private ClientAuthType doClientAuth =
+ ClientAuthType.CLIENT_AUTH_NONE;
private boolean roleIsServer;
private boolean enableSessionCreation = true;
private String host;
@@ -284,9 +272,9 @@
* is created, at which time the new handshaker's state is set.
*
* The readLock is held during readRecord(), which is responsible
- * for reading an InputRecord, decrypting it, and processing it.
+ * for reading an SSLInputRecord, decrypting it, and processing it.
* The readLock ensures that these three steps are done atomically
- * and that once started, no other thread can block on InputRecord.read.
+ * and that once started, no other thread can block on SSLInputRecord.read.
* This is necessary so that processing of close_notify alerts
* from the peer are handled properly.
*/
@@ -294,14 +282,8 @@
final ReentrantLock writeLock = new ReentrantLock();
final private Object readLock = new Object();
- private InputRecord inrec;
-
- /*
- * Crypto state that's reinitialized when the session changes.
- */
- private Authenticator readAuthenticator, writeAuthenticator;
- private CipherBox readCipher, writeCipher;
- // NOTE: compression state would be saved here
+ InputRecord inputRecord;
+ OutputRecord outputRecord;
/*
* security parameters for secure renegotiation.
@@ -368,7 +350,7 @@
/*
* The SSL version associated with this connection.
*/
- private ProtocolVersion protocolVersion = ProtocolVersion.DEFAULT;
+ private ProtocolVersion protocolVersion = ProtocolVersion.DEFAULT_TLS;
/* Class and subclass dynamic debugging support */
private static final Debug debug = Debug.getInstance("ssl");
@@ -390,6 +372,11 @@
*/
private boolean preferLocalCipherSuites = false;
+ /*
+ * The maximum expected network packet size for SSL/TLS/DTLS records.
+ */
+ private int maximumPacketSize = 0;
+
//
// CONSTRUCTORS AND INITIALIZATION CODE
//
@@ -491,7 +478,7 @@
* giving control over the use of SSL client authentication.
*/
SSLSocketImpl(SSLContextImpl context, boolean serverMode,
- CipherSuiteList suites, byte clientAuth,
+ CipherSuiteList suites, ClientAuthType clientAuth,
boolean sessionCreation, ProtocolList protocols,
String identificationProtocol,
AlgorithmConstraints algorithmConstraints,
@@ -594,17 +581,6 @@
*/
roleIsServer = isServer;
connectionState = cs_START;
- receivedCCS = false;
-
- /*
- * default read and write side cipher and MAC support
- *
- * Note: compression support would go here too
- */
- readCipher = CipherBox.NULL;
- readAuthenticator = MAC.NULL;
- writeCipher = CipherBox.NULL;
- writeAuthenticator = MAC.NULL;
// initial security parameters for secure renegotiation
secureRenegotiation = false;
@@ -616,7 +592,10 @@
enabledProtocols =
sslContext.getDefaultProtocolList(roleIsServer);
- inrec = null;
+ inputRecord = new SSLSocketInputRecord();;
+ outputRecord = new SSLSocketOutputRecord();
+
+ maximumPacketSize = outputRecord.getMaxPacketSize();
// save the acc
acc = AccessController.getContext();
@@ -672,6 +651,9 @@
sockInput = super.getInputStream();
sockOutput = super.getOutputStream();
+ inputRecord.setDeliverStream(sockOutput);
+ outputRecord.setDeliverStream(sockOutput);
+
/*
* Move to handshaking state, with pending session initialized
* to defaults and the appropriate kind of handshaker set up.
@@ -696,29 +678,18 @@
//
/*
- * AppOutputStream calls may need to buffer multiple outbound
- * application packets.
+ * Application data record output.
*
- * All other writeRecord() calls will not buffer, so do not hold
- * these records.
+ * Application data can't be sent until the first handshake establishes
+ * a session.
*/
- void writeRecord(OutputRecord r) throws IOException {
- writeRecord(r, false);
- }
-
- /*
- * Record Output. Application data can't be sent until the first
- * handshake establishes a session.
- *
- * NOTE: we let empty records be written as a hook to force some
- * TCP-level activity, notably handshaking, to occur.
- */
- void writeRecord(OutputRecord r, boolean holdRecord) throws IOException {
+ void writeRecord(byte[] source, int offset, int length) throws IOException {
/*
* The loop is in case of HANDSHAKE --> ERROR transitions, etc
*/
- loop:
- while (r.contentType() == Record.ct_application_data) {
+ // Don't bother to check the emptiness of source applicatoin data
+ // before the security connection established.
+ for (boolean readyForApp = false; !readyForApp;) {
/*
* Not all states support passing application data. We
* synchronize access to the connection state, so that
@@ -726,41 +697,43 @@
*/
switch (getConnectionState()) {
- /*
- * We've deferred the initial handshaking till just now,
- * when presumably a thread's decided it's OK to block for
- * longish periods of time for I/O purposes (as well as
- * configured the cipher suites it wants to use).
- */
- case cs_HANDSHAKE:
- performInitialHandshake();
- break;
+ /*
+ * We've deferred the initial handshaking till just now,
+ * when presumably a thread's decided it's OK to block for
+ * longish periods of time for I/O purposes (as well as
+ * configured the cipher suites it wants to use).
+ */
+ case cs_HANDSHAKE:
+ performInitialHandshake();
+ break;
- case cs_DATA:
- case cs_RENEGOTIATE:
- break loop;
+ case cs_DATA:
+ case cs_RENEGOTIATE:
+ readyForApp = true;
+ break;
- case cs_ERROR:
- fatal(Alerts.alert_close_notify,
- "error while writing to socket");
- break; // dummy
+ case cs_ERROR:
+ fatal(Alerts.alert_close_notify,
+ "error while writing to socket");
+ break; // dummy
- case cs_SENT_CLOSE:
- case cs_CLOSED:
- case cs_APP_CLOSED:
- // we should never get here (check in AppOutputStream)
- // this is just a fallback
- if (closeReason != null) {
- throw closeReason;
- } else {
- throw new SocketException("Socket closed");
- }
+ case cs_SENT_CLOSE:
+ case cs_CLOSED:
+ case cs_APP_CLOSED:
+ // we should never get here (check in AppOutputStream)
+ // this is just a fallback
+ if (closeReason != null) {
+ throw closeReason;
+ } else {
+ throw new SocketException("Socket closed");
+ }
- /*
- * Else something's goofy in this state machine's use.
- */
- default:
- throw new SSLProtocolException("State error, send app data");
+ /*
+ * Else something's goofy in this state machine's use.
+ */
+ default:
+ throw new SSLProtocolException(
+ "State error, send app data");
}
}
@@ -772,97 +745,19 @@
// implementations are fragile and don't like to see empty
// records, so this also increases robustness.
//
- if (!r.isEmpty()) {
-
- // If the record is a close notify alert, we need to honor
- // socket option SO_LINGER. Note that we will try to send
- // the close notify even if the SO_LINGER set to zero.
- if (r.isAlert(Alerts.alert_close_notify) && getSoLinger() >= 0) {
-
- // keep and clear the current thread interruption status.
- boolean interrupted = Thread.interrupted();
- try {
- if (writeLock.tryLock(getSoLinger(), TimeUnit.SECONDS)) {
- try {
- writeRecordInternal(r, holdRecord);
- } finally {
- writeLock.unlock();
- }
- } else {
- SSLException ssle = new SSLException(
- "SO_LINGER timeout," +
- " close_notify message cannot be sent.");
-
-
- // For layered, non-autoclose sockets, we are not
- // able to bring them into a usable state, so we
- // treat it as fatal error.
- if (isLayered() && !autoClose) {
- // Note that the alert description is
- // specified as -1, so no message will be send
- // to peer anymore.
- fatal((byte)(-1), ssle);
- } else if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(
- Thread.currentThread().getName() +
- ", received Exception: " + ssle);
- }
-
- // RFC2246 requires that the session becomes
- // unresumable if any connection is terminated
- // without proper close_notify messages with
- // level equal to warning.
- //
- // RFC4346 no longer requires that a session not be
- // resumed if failure to properly close a connection.
- //
- // We choose to make the session unresumable if
- // failed to send the close_notify message.
- //
- sess.invalidate();
- }
- } catch (InterruptedException ie) {
- // keep interrupted status
- interrupted = true;
- }
-
- // restore the interrupted status
- if (interrupted) {
- Thread.currentThread().interrupt();
- }
- } else {
- writeLock.lock();
- try {
- writeRecordInternal(r, holdRecord);
- } finally {
- writeLock.unlock();
- }
+ if (length > 0) {
+ writeLock.lock();
+ try {
+ outputRecord.deliver(source, offset, length);
+ } catch (SSLHandshakeException she) {
+ // may be record sequence number overflow
+ fatal(Alerts.alert_handshake_failure, she);
+ } catch (IOException e) {
+ fatal(Alerts.alert_unexpected_message, e);
+ } finally {
+ writeLock.unlock();
}
}
- }
-
- private void writeRecordInternal(OutputRecord r,
- boolean holdRecord) throws IOException {
-
- // r.compress(c);
- r.encrypt(writeAuthenticator, writeCipher);
-
- if (holdRecord) {
- // If we were requested to delay the record due to possibility
- // of Nagle's being active when finally got to writing, and
- // it's actually not, we don't really need to delay it.
- if (getTcpNoDelay()) {
- holdRecord = false;
- } else {
- // We need to hold the record, so let's provide
- // a per-socket place to do it.
- if (heldRecordBuffer == null) {
- // Likely only need 37 bytes.
- heldRecordBuffer = new ByteArrayOutputStream(40);
- }
- }
- }
- r.write(sockOutput, holdRecord, heldRecordBuffer);
/*
* Check the sequence number state
@@ -874,51 +769,173 @@
* when there is enough sequence number space left to
* handle a few more records, so the sequence number
* of the last record cannot be wrapped.
+ *
+ * Don't bother to kickstart the renegotiation when the
+ * local is asking for it.
*/
- if (connectionState < cs_ERROR) {
- checkSequenceNumber(writeAuthenticator, r.contentType());
- }
+ if ((connectionState == cs_DATA) && outputRecord.seqNumIsHuge()) {
+ /*
+ * Ask for renegotiation when need to renew sequence number.
+ *
+ * Don't bother to kickstart the renegotiation when the local is
+ * asking for it.
+ */
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", request renegotiation " +
+ "to avoid sequence number overflow");
+ }
- // turn off the flag of the first application record
- if (isFirstAppOutputRecord &&
- r.contentType() == Record.ct_application_data) {
- isFirstAppOutputRecord = false;
+ startHandshake();
}
}
/*
- * Need to split the payload except the following cases:
- *
- * 1. protocol version is TLS 1.1 or later;
- * 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
- * 3. the payload is the first application record of a freshly
- * negotiated TLS session.
- * 4. the CBC protection is disabled;
- *
- * More details, please refer to AppOutputStream.write(byte[], int, int).
+ * Alert record output.
*/
- boolean needToSplitPayload() {
- writeLock.lock();
- try {
- return (protocolVersion.v <= ProtocolVersion.TLS10.v) &&
- writeCipher.isCBCMode() && !isFirstAppOutputRecord &&
- Record.enableCBCProtection;
- } finally {
- writeLock.unlock();
+ void writeAlert(byte level, byte description) throws IOException {
+
+ // If the record is a close notify alert, we need to honor
+ // socket option SO_LINGER. Note that we will try to send
+ // the close notify even if the SO_LINGER set to zero.
+ if ((description == Alerts.alert_close_notify) && getSoLinger() >= 0) {
+
+ // keep and clear the current thread interruption status.
+ boolean interrupted = Thread.interrupted();
+ try {
+ if (writeLock.tryLock(getSoLinger(), TimeUnit.SECONDS)) {
+ try {
+ outputRecord.encodeAlert(level, description);
+ } finally {
+ writeLock.unlock();
+ }
+ } else {
+ SSLException ssle = new SSLException(
+ "SO_LINGER timeout," +
+ " close_notify message cannot be sent.");
+
+
+ // For layered, non-autoclose sockets, we are not
+ // able to bring them into a usable state, so we
+ // treat it as fatal error.
+ if (isLayered() && !autoClose) {
+ // Note that the alert description is
+ // specified as -1, so no message will be send
+ // to peer anymore.
+ fatal((byte)(-1), ssle);
+ } else if ((debug != null) && Debug.isOn("ssl")) {
+ System.out.println(
+ Thread.currentThread().getName() +
+ ", received Exception: " + ssle);
+ }
+
+ // RFC2246 requires that the session becomes
+ // unresumable if any connection is terminated
+ // without proper close_notify messages with
+ // level equal to warning.
+ //
+ // RFC4346 no longer requires that a session not be
+ // resumed if failure to properly close a connection.
+ //
+ // We choose to make the session unresumable if
+ // failed to send the close_notify message.
+ //
+ sess.invalidate();
+ }
+ } catch (InterruptedException ie) {
+ // keep interrupted status
+ interrupted = true;
+ }
+
+ // restore the interrupted status
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ } else {
+ writeLock.lock();
+ try {
+ outputRecord.encodeAlert(level, description);
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ // Don't bother to check sequence number overlap here. If sequence
+ // number is huge, there should be enough sequence number space to
+ // request renegotiation in next application data read and write.
+ }
+
+
+ int bytesInCompletePacket() throws IOException {
+ if (getConnectionState() == cs_HANDSHAKE) {
+ performInitialHandshake();
+ }
+
+ synchronized (readLock) {
+ int state = getConnectionState();
+ if ((state == cs_CLOSED) ||
+ (state == cs_ERROR) || (state == cs_APP_CLOSED)) {
+ return -1;
+ }
+
+ try {
+ return inputRecord.bytesInCompletePacket(sockInput);
+ } catch (EOFException eofe) {
+ boolean handshaking = (connectionState <= cs_HANDSHAKE);
+ boolean rethrow = requireCloseNotify || handshaking;
+ if ((debug != null) && Debug.isOn("ssl")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", received EOFException: "
+ + (rethrow ? "error" : "ignored"));
+ }
+
+ if (!rethrow) {
+ // treat as if we had received a close_notify
+ closeInternal(false);
+ } else {
+ SSLException e;
+ if (handshaking) {
+ e = new SSLHandshakeException(
+ "Remote host terminated the handshake");
+ } else {
+ e = new SSLProtocolException(
+ "Remote host terminated the handshake");
+ }
+ e.initCause(eofe);
+ throw e;
+ }
+ }
+
+ return -1;
}
}
+ // the caller have synchronized readLock
+ void expectingFinishFlight() {
+ inputRecord.expectingFinishFlight();
+ }
+
/*
- * Read an application data record. Alerts and handshake
- * messages are handled directly.
+ * Read an application data record.
+ *
+ * Alerts and handshake messages are internally handled directly.
*/
- void readDataRecord(InputRecord r) throws IOException {
+ int readRecord(ByteBuffer buffer) throws IOException {
if (getConnectionState() == cs_HANDSHAKE) {
performInitialHandshake();
}
- readRecord(r, true);
+
+ return readRecord(buffer, true);
}
+ /*
+ * Read a record, no application data input required.
+ *
+ * Alerts and handshake messages are internally handled directly.
+ */
+ int readRecord(boolean needAppData) throws IOException {
+ return readRecord(null, needAppData);
+ }
/*
* Clear the pipeline of records from the peer, optionally returning
@@ -929,11 +946,11 @@
* Don't synchronize (this) during a blocking read() since it
* protects data which is accessed on the write side as well.
*/
- private void readRecord(InputRecord r, boolean needAppData)
+ private int readRecord(ByteBuffer buffer, boolean needAppData)
throws IOException {
int state;
- // readLock protects reading and processing of an InputRecord.
+ // readLock protects reading and processing of an SSLInputRecord.
// It keeps the reading from sockInput and processing of the record
// atomic so that no two threads can be blocked on the
// read from the same input stream at the same time.
@@ -944,306 +961,282 @@
//
// Use readLock instead of 'this' for locking because
// 'this' also protects data accessed during writing.
- synchronized (readLock) {
- /*
- * Read and handle records ... return application data
- * ONLY if it's needed.
- */
-
- while (((state = getConnectionState()) != cs_CLOSED) &&
- (state != cs_ERROR) && (state != cs_APP_CLOSED)) {
+ synchronized (readLock) {
/*
- * Read a record ... maybe emitting an alert if we get a
- * comprehensible but unsupported "hello" message during
- * format checking (e.g. V2).
- */
- try {
- r.setAppDataValid(false);
- r.read(sockInput, sockOutput);
- } catch (SSLProtocolException e) {
- try {
- fatal(Alerts.alert_unexpected_message, e);
- } catch (IOException x) {
- // discard this exception
- }
- throw e;
- } catch (EOFException eof) {
- boolean handshaking = (getConnectionState() <= cs_HANDSHAKE);
- boolean rethrow = requireCloseNotify || handshaking;
- if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(Thread.currentThread().getName() +
- ", received EOFException: "
- + (rethrow ? "error" : "ignored"));
- }
- if (rethrow) {
- SSLException e;
- if (handshaking) {
- e = new SSLHandshakeException
- ("Remote host closed connection during handshake");
- } else {
- e = new SSLProtocolException
- ("Remote host closed connection incorrectly");
- }
- e.initCause(eof);
- throw e;
- } else {
- // treat as if we had received a close_notify
- closeInternal(false);
- continue;
- }
- }
-
-
- /*
- * The basic SSLv3 record protection involves (optional)
- * encryption for privacy, and an integrity check ensuring
- * data origin authentication. We do them both here, and
- * throw a fatal alert if the integrity check fails.
- */
- try {
- r.decrypt(readAuthenticator, readCipher);
- } catch (BadPaddingException e) {
- byte alertType = (r.contentType() == Record.ct_handshake)
- ? Alerts.alert_handshake_failure
- : Alerts.alert_bad_record_mac;
- fatal(alertType, e.getMessage(), e);
- }
-
- // if (!r.decompress(c))
- // fatal(Alerts.alert_decompression_failure,
- // "decompression failure");
-
- /*
- * Process the record.
+ * Read and handle records ... return application data
+ * ONLY if it's needed.
*/
- synchronized (this) {
- switch (r.contentType()) {
- case Record.ct_handshake:
- /*
- * Handshake messages always go to a pending session
- * handshaker ... if there isn't one, create one. This
- * must work asynchronously, for renegotiation.
- *
- * NOTE that handshaking will either resume a session
- * which was in the cache (and which might have other
- * connections in it already), or else will start a new
- * session (new keys exchanged) with just this connection
- * in it.
- */
- initHandshaker();
- if (!handshaker.activated()) {
- // prior to handshaking, activate the handshake
- if (connectionState == cs_RENEGOTIATE) {
- // don't use SSLv2Hello when renegotiating
- handshaker.activate(protocolVersion);
- } else {
- handshaker.activate(null);
- }
- }
-
- /*
- * process the handshake record ... may contain just
- * a partial handshake message or multiple messages.
- *
- * The handshaker state machine will ensure that it's
- * a finished message.
- */
- handshaker.process_record(r, expectingFinished);
- expectingFinished = false;
+ Plaintext plainText = null;
+ while (((state = getConnectionState()) != cs_CLOSED) &&
+ (state != cs_ERROR) && (state != cs_APP_CLOSED)) {
+ // clean the buffer
+ if (buffer != null) {
+ buffer.clear();
+ }
- if (handshaker.invalidated) {
- handshaker = null;
- receivedCCS = false;
- // if state is cs_RENEGOTIATE, revert it to cs_DATA
- if (connectionState == cs_RENEGOTIATE) {
- connectionState = cs_DATA;
+ /*
+ * Read a record ... maybe emitting an alert if we get a
+ * comprehensible but unsupported "hello" message during
+ * format checking (e.g. V2).
+ */
+ try {
+ plainText = inputRecord.decode(sockInput, buffer);
+ } catch (BadPaddingException bpe) {
+ byte alertType = (state != cs_DATA) ?
+ Alerts.alert_handshake_failure :
+ Alerts.alert_bad_record_mac;
+ fatal(alertType, bpe.getMessage(), bpe);
+ } catch (SSLProtocolException spe) {
+ try {
+ fatal(Alerts.alert_unexpected_message, spe);
+ } catch (IOException x) {
+ // discard this exception, throw the original exception
+ }
+ throw spe;
+ } catch (SSLHandshakeException she) {
+ // may be record sequence number overflow
+ fatal(Alerts.alert_handshake_failure, she);
+ } catch (EOFException eof) {
+ boolean handshaking = (connectionState <= cs_HANDSHAKE);
+ boolean rethrow = requireCloseNotify || handshaking;
+ if ((debug != null) && Debug.isOn("ssl")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", received EOFException: "
+ + (rethrow ? "error" : "ignored"));
+ }
+ if (rethrow) {
+ SSLException e;
+ if (handshaking) {
+ e = new SSLHandshakeException(
+ "Remote host terminated the handshake");
+ } else {
+ e = new SSLProtocolException(
+ "Remote host terminated the connection");
}
- } else if (handshaker.isDone()) {
- // reset the parameters for secure renegotiation.
- secureRenegotiation =
- handshaker.isSecureRenegotiation();
- clientVerifyData = handshaker.getClientVerifyData();
- serverVerifyData = handshaker.getServerVerifyData();
-
- sess = handshaker.getSession();
- handshakeSession = null;
- handshaker = null;
- connectionState = cs_DATA;
- receivedCCS = false;
-
- //
- // Tell folk about handshake completion, but do
- // it in a separate thread.
- //
- if (handshakeListeners != null) {
- HandshakeCompletedEvent event =
- new HandshakeCompletedEvent(this, sess);
-
- Thread t = new ManagedLocalsThread(
- new NotifyHandshake(
- handshakeListeners.entrySet(), event),
- "HandshakeCompletedNotify-Thread");
- t.start();
- }
- }
-
- if (needAppData || connectionState != cs_DATA) {
+ e.initCause(eof);
+ throw e;
+ } else {
+ // treat as if we had received a close_notify
+ closeInternal(false);
continue;
}
- break;
+ }
+
+ // PlainText should never be null. Process input record.
+ int volume = processInputRecord(plainText, needAppData);
+
+ if (plainText.contentType == Record.ct_application_data) {
+ return volume;
+ }
+
+ if (plainText.contentType == Record.ct_handshake) {
+ if (!needAppData && connectionState == cs_DATA) {
+ return volume;
+ } // otherwise, need to read more for app data.
+ }
- case Record.ct_application_data:
- // Pass this right back up to the application.
- if (connectionState != cs_DATA
- && connectionState != cs_RENEGOTIATE
- && connectionState != cs_SENT_CLOSE) {
- throw new SSLProtocolException(
- "Data received in non-data state: " +
- connectionState);
- }
- if (expectingFinished) {
- throw new SSLProtocolException
- ("Expecting finished message, received data");
- }
- if (!needAppData) {
- throw new SSLException("Discarding app data");
- }
+ // continue to read more net data
+ } // while
- r.setAppDataValid(true);
- break;
+ //
+ // couldn't read, due to some kind of error
+ //
+ return -1;
+ } // readLock synchronization
+ }
+
+ /*
+ * Process the plainText input record.
+ */
+ private synchronized int processInputRecord(
+ Plaintext plainText, boolean needAppData) throws IOException {
- case Record.ct_alert:
- recvAlert(r);
- continue;
+ /*
+ * Process the record.
+ */
+ int volume = 0; // no application data
+ switch (plainText.contentType) {
+ case Record.ct_handshake:
+ /*
+ * Handshake messages always go to a pending session
+ * handshaker ... if there isn't one, create one. This
+ * must work asynchronously, for renegotiation.
+ *
+ * NOTE that handshaking will either resume a session
+ * which was in the cache (and which might have other
+ * connections in it already), or else will start a new
+ * session (new keys exchanged) with just this connection
+ * in it.
+ */
+ initHandshaker();
+ if (!handshaker.activated()) {
+ // prior to handshaking, activate the handshake
+ if (connectionState == cs_RENEGOTIATE) {
+ // don't use SSLv2Hello when renegotiating
+ handshaker.activate(protocolVersion);
+ } else {
+ handshaker.activate(null);
+ }
+ }
- case Record.ct_change_cipher_spec:
- if ((connectionState != cs_HANDSHAKE
- && connectionState != cs_RENEGOTIATE)
- || !handshaker.sessionKeysCalculated()
- || receivedCCS) {
- // For the CCS message arriving in the wrong state
- fatal(Alerts.alert_unexpected_message,
- "illegal change cipher spec msg, conn state = "
- + connectionState + ", handshake state = "
- + handshaker.state);
- } else if (r.available() != 1 || r.read() != 1) {
- // For structural/content issues with the CCS
- fatal(Alerts.alert_unexpected_message,
- "Malformed change cipher spec msg");
+ /*
+ * process the handshake record ... may contain just
+ * a partial handshake message or multiple messages.
+ *
+ * The handshaker state machine will ensure that it's
+ * a finished message.
+ */
+ handshaker.processRecord(plainText.fragment, expectingFinished);
+ expectingFinished = false;
+
+ if (handshaker.invalidated) {
+ handshaker = null;
+ inputRecord.setHandshakeHash(null);
+ outputRecord.setHandshakeHash(null);
+
+ // if state is cs_RENEGOTIATE, revert it to cs_DATA
+ if (connectionState == cs_RENEGOTIATE) {
+ connectionState = cs_DATA;
}
+ } else if (handshaker.isDone()) {
+ // reset the parameters for secure renegotiation.
+ secureRenegotiation =
+ handshaker.isSecureRenegotiation();
+ clientVerifyData = handshaker.getClientVerifyData();
+ serverVerifyData = handshaker.getServerVerifyData();
- // Once we've received CCS, update the flag.
- // If the remote endpoint sends it again in this handshake
- // we won't process it.
- receivedCCS = true;
+ sess = handshaker.getSession();
+ handshakeSession = null;
+ handshaker = null;
+ inputRecord.setHandshakeHash(null);
+ outputRecord.setHandshakeHash(null);
+ connectionState = cs_DATA;
//
- // The first message after a change_cipher_spec
- // record MUST be a "Finished" handshake record,
- // else it's a protocol violation. We force this
- // to be checked by a minor tweak to the state
- // machine.
+ // Tell folk about handshake completion, but do
+ // it in a separate thread.
//
- changeReadCiphers();
- // next message MUST be a finished message
- expectingFinished = true;
- continue;
+ if (handshakeListeners != null) {
+ HandshakeCompletedEvent event =
+ new HandshakeCompletedEvent(this, sess);
+
+ Thread thread = new ManagedLocalsThread(
+ new NotifyHandshake(
+ handshakeListeners.entrySet(), event),
+ "HandshakeCompletedNotify-Thread");
+ thread.start();
+ }
+ }
+
+ break;
- default:
- //
- // TLS requires that unrecognized records be ignored.
- //
- if (debug != null && Debug.isOn("ssl")) {
- System.out.println(Thread.currentThread().getName() +
- ", Received record type: "
- + r.contentType());
- }
- continue;
- } // switch
+ case Record.ct_application_data:
+ if (connectionState != cs_DATA
+ && connectionState != cs_RENEGOTIATE
+ && connectionState != cs_SENT_CLOSE) {
+ throw new SSLProtocolException(
+ "Data received in non-data state: " +
+ connectionState);
+ }
+ if (expectingFinished) {
+ throw new SSLProtocolException
+ ("Expecting finished message, received data");
+ }
+ if (!needAppData) {
+ throw new SSLException("Discarding app data");
+ }
+
+ volume = plainText.fragment.remaining();
+ break;
+
+ case Record.ct_alert:
+ recvAlert(plainText.fragment);
+ break;
- /*
- * Check the sequence number state
- *
- * Note that in order to maintain the connection I/O
- * properly, we check the sequence number after the last
- * record reading process. As we request renegotiation
- * or close the connection for wrapped sequence number
- * when there is enough sequence number space left to
- * handle a few more records, so the sequence number
- * of the last record cannot be wrapped.
- */
- if (connectionState < cs_ERROR) {
- checkSequenceNumber(readAuthenticator, r.contentType());
- }
+ case Record.ct_change_cipher_spec:
+ if ((connectionState != cs_HANDSHAKE
+ && connectionState != cs_RENEGOTIATE)) {
+ // For the CCS message arriving in the wrong state
+ fatal(Alerts.alert_unexpected_message,
+ "illegal change cipher spec msg, conn state = "
+ + connectionState);
+ } else if (plainText.fragment.remaining() != 1
+ || plainText.fragment.get() != 1) {
+ // For structural/content issues with the CCS
+ fatal(Alerts.alert_unexpected_message,
+ "Malformed change cipher spec msg");
+ }
- return;
- } // synchronized (this)
- }
+ //
+ // The first message after a change_cipher_spec
+ // record MUST be a "Finished" handshake record,
+ // else it's a protocol violation. We force this
+ // to be checked by a minor tweak to the state
+ // machine.
+ //
+ handshaker.receiveChangeCipherSpec();
- //
- // couldn't read, due to some kind of error
- //
- r.close();
- return;
- } // synchronized (readLock)
- }
+ CipherBox readCipher;
+ Authenticator readAuthenticator;
+ try {
+ readCipher = handshaker.newReadCipher();
+ readAuthenticator = handshaker.newReadAuthenticator();
+ } catch (GeneralSecurityException e) {
+ // can't happen
+ throw new SSLException("Algorithm missing: ", e);
+ }
+ inputRecord.changeReadCiphers(readAuthenticator, readCipher);
- /**
- * Check the sequence number state
- *
- * RFC 4346 states that, "Sequence numbers are of type uint64 and
- * may not exceed 2^64-1. Sequence numbers do not wrap. If a TLS
- * implementation would need to wrap a sequence number, it must
- * renegotiate instead."
- */
- private void checkSequenceNumber(Authenticator authenticator, byte type)
- throws IOException {
+ // next message MUST be a finished message
+ expectingFinished = true;
+
+ break;
- /*
- * Don't bother to check the sequence number for error or
- * closed connections, or NULL MAC.
- */
- if (connectionState >= cs_ERROR || authenticator == MAC.NULL) {
- return;
+ default:
+ //
+ // TLS requires that unrecognized records be ignored.
+ //
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", Received record type: " + plainText.contentType);
+ }
+ break;
}
/*
- * Conservatively, close the connection immediately when the
- * sequence number is close to overflow
+ * Check the sequence number state
+ *
+ * Note that in order to maintain the connection I/O
+ * properly, we check the sequence number after the last
+ * record reading process. As we request renegotiation
+ * or close the connection for wrapped sequence number
+ * when there is enough sequence number space left to
+ * handle a few more records, so the sequence number
+ * of the last record cannot be wrapped.
+ *
+ * Don't bother to kickstart the renegotiation when the
+ * local is asking for it.
*/
- if (authenticator.seqNumOverflow()) {
+ if ((connectionState == cs_DATA) && inputRecord.seqNumIsHuge()) {
/*
- * TLS protocols do not define a error alert for sequence
- * number overflow. We use handshake_failure error alert
- * for handshaking and bad_record_mac for other records.
+ * Ask for renegotiation when need to renew sequence number.
+ *
+ * Don't bother to kickstart the renegotiation when the local is
+ * asking for it.
*/
if (debug != null && Debug.isOn("ssl")) {
System.out.println(Thread.currentThread().getName() +
- ", sequence number extremely close to overflow " +
- "(2^64-1 packets). Closing connection.");
-
- }
-
- fatal(Alerts.alert_handshake_failure, "sequence number overflow");
- }
-
- /*
- * Ask for renegotiation when need to renew sequence number.
- *
- * Don't bother to kickstart the renegotiation when the local is
- * asking for it.
- */
- if ((type != Record.ct_handshake) && authenticator.seqNumIsHuge()) {
- if (debug != null && Debug.isOn("ssl")) {
- System.out.println(Thread.currentThread().getName() +
", request renegotiation " +
"to avoid sequence number overflow");
}
startHandshake();
}
+
+ return volume;
}
+
//
// HANDSHAKE RELATED CODE
//
@@ -1323,6 +1316,7 @@
secureRenegotiation, clientVerifyData, serverVerifyData);
handshaker.setSNIServerNames(serverNames);
}
+ handshaker.setMaximumPacketSize(maximumPacketSize);
handshaker.setEnabledCipherSuites(enabledCipherSuites);
handshaker.setEnableSessionCreation(enableSessionCreation);
}
@@ -1342,29 +1336,12 @@
kickstartHandshake();
/*
- * All initial handshaking goes through this
- * InputRecord until we have a valid SSL connection.
- * Once initial handshaking is finished, AppInputStream's
- * InputRecord can handle any future renegotiation.
+ * All initial handshaking goes through this operation
+ * until we have a valid SSL connection.
*
- * Keep this local so that it goes out of scope and is
- * eventually GC'd.
+ * Handle handshake messages only, need no application data.
*/
- if (inrec == null) {
- inrec = new InputRecord();
-
- /*
- * Grab the characteristics already assigned to
- * AppInputStream's InputRecord. Enable checking for
- * SSLv2 hellos on this first handshake.
- */
- inrec.setHandshakeHash(input.r.getHandshakeHash());
- inrec.setHelloVersion(input.r.getHelloVersion());
- inrec.enableFormatChecks();
- }
-
- readRecord(inrec, false);
- inrec = null;
+ readRecord(false);
}
}
}
@@ -1482,9 +1459,6 @@
} else {
// we want to renegotiate, send hello request
handshaker.kickstart();
- // hello request is not included in the handshake
- // hashes, reset them
- handshaker.handshakeHash.reset();
}
}
}
@@ -1547,7 +1521,7 @@
}
}
- protected void closeSocket() throws IOException {
+ private void closeSocket() throws IOException {
if ((debug != null) && Debug.isOn("ssl")) {
System.out.println(Thread.currentThread().getName() +
@@ -1591,6 +1565,23 @@
", called close()");
}
closeInternal(true); // caller is initiating close
+
+ // Clearup the resources.
+ try {
+ synchronized (readLock) {
+ inputRecord.close();
+ }
+
+ writeLock.lock();
+ try {
+ outputRecord.close();
+ } finally {
+ writeLock.unlock();
+ }
+ } catch (IOException ioe) {
+ // ignore
+ }
+
setConnectionState(cs_APP_CLOSED);
}
@@ -1714,19 +1705,17 @@
// notify any threads waiting for the closing to finish
this.notifyAll();
}
- if (closeSocketCalled) {
- // Dispose of ciphers since we've closed socket
- disposeCiphers();
- }
+
if (cachedThrowable != null) {
/*
* Rethrow the error to the calling method
* The Throwable caught can only be an Error or RuntimeException
*/
- if (cachedThrowable instanceof Error)
- throw (Error) cachedThrowable;
- if (cachedThrowable instanceof RuntimeException)
- throw (RuntimeException) cachedThrowable;
+ if (cachedThrowable instanceof Error) {
+ throw (Error)cachedThrowable;
+ } else if (cachedThrowable instanceof RuntimeException) {
+ throw (RuntimeException)cachedThrowable;
+ } // Otherwise, unlikely
}
}
}
@@ -1750,19 +1739,14 @@
while (((state = getConnectionState()) != cs_CLOSED) &&
(state != cs_ERROR) && (state != cs_APP_CLOSED)) {
- // create the InputRecord if it isn't initialized.
- if (inrec == null) {
- inrec = new InputRecord();
- }
// Ask for app data and then throw it away
try {
- readRecord(inrec, true);
+ readRecord(true);
} catch (SocketTimeoutException e) {
// if time out, ignore the exception and continue
}
}
- inrec = null;
} catch (IOException e) {
if (debug != null && Debug.isOn("ssl")) {
System.out.println(Thread.currentThread().getName() +
@@ -1774,24 +1758,6 @@
}
}
- /**
- * Called by closeInternal() only. Be sure to consider the
- * synchronization locks carefully before calling it elsewhere.
- */
- private void disposeCiphers() {
- // See comment in changeReadCiphers()
- synchronized (readLock) {
- readCipher.dispose();
- }
- // See comment in changeReadCiphers()
- writeLock.lock();
- try {
- writeCipher.dispose();
- } finally {
- writeLock.unlock();
- }
- }
-
//
// EXCEPTION AND ALERT HANDLING
//
@@ -1853,7 +1819,7 @@
// need to perform error shutdown
boolean isSSLException = (e instanceof SSLException);
- if ((isSSLException == false) && (e instanceof IOException)) {
+ if ((!isSSLException) && (e instanceof IOException)) {
// IOException from the socket
// this means the TCP connection is already dead
// we call fatal just to set the error status
@@ -1903,9 +1869,14 @@
*/
synchronized void fatal(byte description, String diagnostic,
Throwable cause) throws IOException {
- if ((input != null) && (input.r != null)) {
- input.r.close();
+
+ // Be care of deadlock. Please don't synchronize readLock.
+ try {
+ inputRecord.close();
+ } catch (IOException ioe) {
+ // ignore
}
+
sess.invalidate();
if (handshakeSession != null) {
handshakeSession.invalidate();
@@ -1945,15 +1916,12 @@
* Clean up our side.
*/
closeSocket();
- // Another thread may have disposed the ciphers during closing
- if (connectionState < cs_CLOSED) {
- connectionState = (oldState == cs_APP_CLOSED) ? cs_APP_CLOSED
- : cs_CLOSED;
- // We should lock readLock and writeLock if no deadlock risks.
- // See comment in changeReadCiphers()
- readCipher.dispose();
- writeCipher.dispose();
+ // Be care of deadlock. Please don't synchronize writeLock.
+ try {
+ outputRecord.close();
+ } catch (IOException ioe) {
+ // ignore
}
throw closeReason;
@@ -1964,9 +1932,10 @@
* Process an incoming alert ... caller must already have synchronized
* access to "this".
*/
- private void recvAlert(InputRecord r) throws IOException {
- byte level = (byte)r.read();
- byte description = (byte)r.read();
+ private void recvAlert(ByteBuffer fragment) throws IOException {
+ byte level = fragment.get();
+ byte description = fragment.get();
+
if (description == -1) { // check for short message
fatal(Alerts.alert_illegal_parameter, "Short alert message");
}
@@ -2029,14 +1998,14 @@
// For initial handshaking, don't send alert message to peer if
// handshaker has not started.
- if (connectionState == cs_HANDSHAKE &&
- (handshaker == null || !handshaker.started())) {
+ //
+ // Shall we send an fatal alter to terminate the connection gracefully?
+ if (connectionState <= cs_HANDSHAKE &&
+ (handshaker == null || !handshaker.started() ||
+ !handshaker.activated())) {
return;
}
- OutputRecord r = new OutputRecord(Record.ct_alert);
- r.setVersion(protocolVersion);
-
boolean useDebug = debug != null && Debug.isOn("ssl");
if (useDebug) {
synchronized (System.out) {
@@ -2054,10 +2023,8 @@
}
}
- r.write(level);
- r.write(description);
try {
- writeRecord(r);
+ writeAlert(level, description);
} catch (IOException e) {
if (useDebug) {
System.out.println(Thread.currentThread().getName() +
@@ -2070,61 +2037,10 @@
// VARIOUS OTHER METHODS
//
- /*
- * When a connection finishes handshaking by enabling use of a newly
- * negotiated session, each end learns about it in two halves (read,
- * and write). When both read and write ciphers have changed, and the
- * last handshake message has been read, the connection has joined
- * (rejoined) the new session.
- *
- * NOTE: The SSLv3 spec is rather unclear on the concepts here.
- * Sessions don't change once they're established (including cipher
- * suite and master secret) but connections can join them (and leave
- * them). They're created by handshaking, though sometime handshaking
- * causes connections to join up with pre-established sessions.
- */
- private void changeReadCiphers() throws SSLException {
- if (connectionState != cs_HANDSHAKE
- && connectionState != cs_RENEGOTIATE) {
- throw new SSLProtocolException(
- "State error, change cipher specs");
- }
-
- // ... create decompressor
-
- CipherBox oldCipher = readCipher;
-
- try {
- readCipher = handshaker.newReadCipher();
- readAuthenticator = handshaker.newReadAuthenticator();
- } catch (GeneralSecurityException e) {
- // "can't happen"
- throw new SSLException("Algorithm missing: ", e);
- }
-
- /*
- * Dispose of any intermediate state in the underlying cipher.
- * For PKCS11 ciphers, this will release any attached sessions,
- * and thus make finalization faster.
- *
- * Since MAC's doFinal() is called for every SSL/TLS packet, it's
- * not necessary to do the same with MAC's.
- */
- oldCipher.dispose();
- }
-
// used by Handshaker
- void changeWriteCiphers() throws SSLException {
- if (connectionState != cs_HANDSHAKE
- && connectionState != cs_RENEGOTIATE) {
- throw new SSLProtocolException(
- "State error, change cipher specs");
- }
-
- // ... create compressor
-
- CipherBox oldCipher = writeCipher;
-
+ void changeWriteCiphers() throws IOException {
+ Authenticator writeAuthenticator;
+ CipherBox writeCipher;
try {
writeCipher = handshaker.newWriteCipher();
writeAuthenticator = handshaker.newWriteAuthenticator();
@@ -2132,12 +2048,7 @@
// "can't happen"
throw new SSLException("Algorithm missing: ", e);
}
-
- // See comment above.
- oldCipher.dispose();
-
- // reset the flag of the first application record
- isFirstAppOutputRecord = true;
+ outputRecord.changeWriteCiphers(writeAuthenticator, writeCipher);
}
/*
@@ -2146,7 +2057,7 @@
*/
synchronized void setVersion(ProtocolVersion protocolVersion) {
this.protocolVersion = protocolVersion;
- output.r.setVersion(protocolVersion);
+ outputRecord.setVersion(protocolVersion);
}
synchronized String getHost() {
@@ -2245,6 +2156,10 @@
}
synchronized void setHandshakeSession(SSLSessionImpl session) {
+ // update the fragment size, which may be negotiated during handshaking
+ inputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize());
+ outputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize());
+
handshakeSession = session;
}
@@ -2285,8 +2200,8 @@
*/
@Override
synchronized public void setNeedClientAuth(boolean flag) {
- doClientAuth = (flag ?
- SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none);
+ doClientAuth = (flag ? ClientAuthType.CLIENT_AUTH_REQUIRED :
+ ClientAuthType.CLIENT_AUTH_NONE);
if ((handshaker != null) &&
(handshaker instanceof ServerHandshaker) &&
@@ -2297,7 +2212,7 @@
@Override
synchronized public boolean getNeedClientAuth() {
- return (doClientAuth == SSLEngineImpl.clauth_required);
+ return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED);
}
/**
@@ -2310,8 +2225,8 @@
*/
@Override
synchronized public void setWantClientAuth(boolean flag) {
- doClientAuth = (flag ?
- SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none);
+ doClientAuth = (flag ? ClientAuthType.CLIENT_AUTH_REQUESTED :
+ ClientAuthType.CLIENT_AUTH_NONE);
if ((handshaker != null) &&
(handshaker instanceof ServerHandshaker) &&
@@ -2322,7 +2237,7 @@
@Override
synchronized public boolean getWantClientAuth() {
- return (doClientAuth == SSLEngineImpl.clauth_requested);
+ return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED);
}
@@ -2339,13 +2254,22 @@
case cs_START:
/*
* If we need to change the socket mode and the enabled
- * protocols haven't specifically been set by the user,
- * change them to the corresponding default ones.
+ * protocols and cipher suites haven't specifically been
+ * set by the user, change them to the corresponding
+ * default ones.
*/
- if (roleIsServer != (!flag) &&
- sslContext.isDefaultProtocolList(enabledProtocols)) {
- enabledProtocols = sslContext.getDefaultProtocolList(!flag);
+ if (roleIsServer != (!flag)) {
+ if (sslContext.isDefaultProtocolList(enabledProtocols)) {
+ enabledProtocols =
+ sslContext.getDefaultProtocolList(!flag);
+ }
+
+ if (sslContext.isDefaultCipherSuiteList(enabledCipherSuites)) {
+ enabledCipherSuites =
+ sslContext.getDefaultCipherSuiteList(!flag);
+ }
}
+
roleIsServer = !flag;
break;
@@ -2361,13 +2285,23 @@
if (!handshaker.activated()) {
/*
* If we need to change the socket mode and the enabled
- * protocols haven't specifically been set by the user,
- * change them to the corresponding default ones.
+ * protocols and cipher suites haven't specifically been
+ * set by the user, change them to the corresponding
+ * default ones.
*/
- if (roleIsServer != (!flag) &&
- sslContext.isDefaultProtocolList(enabledProtocols)) {
- enabledProtocols = sslContext.getDefaultProtocolList(!flag);
+ if (roleIsServer != (!flag)) {
+ if (sslContext.isDefaultProtocolList(enabledProtocols)) {
+ enabledProtocols =
+ sslContext.getDefaultProtocolList(!flag);
+ }
+
+ if (sslContext.isDefaultCipherSuiteList(
+ enabledCipherSuites)) {
+ enabledCipherSuites =
+ sslContext.getDefaultCipherSuiteList(!flag);
+ }
}
+
roleIsServer = !flag;
connectionState = cs_START;
initHandshaker();
@@ -2535,6 +2469,9 @@
params.setSNIMatchers(sniMatchers);
params.setServerNames(serverNames);
params.setUseCipherSuitesOrder(preferLocalCipherSuites);
+ params.setMaximumPacketSize(maximumPacketSize);
+
+ // DTLS handshake retransmissions parameter does not apply here.
return params;
}
@@ -2550,6 +2487,16 @@
identificationProtocol = params.getEndpointIdentificationAlgorithm();
algorithmConstraints = params.getAlgorithmConstraints();
preferLocalCipherSuites = params.getUseCipherSuitesOrder();
+ maximumPacketSize = params.getMaximumPacketSize();
+
+ // DTLS handshake retransmissions parameter does not apply here.
+
+ if (maximumPacketSize != 0) {
+ outputRecord.changePacketSize(maximumPacketSize);
+ } else {
+ // use the implicit maximum packet size.
+ maximumPacketSize = outputRecord.getMaxPacketSize();
+ }
List<SNIServerName> sniNames = params.getServerNames();
if (sniNames != null) {
@@ -2564,6 +2511,7 @@
if ((handshaker != null) && !handshaker.started()) {
handshaker.setIdentificationProtocol(identificationProtocol);
handshaker.setAlgorithmConstraints(algorithmConstraints);
+ handshaker.setMaximumPacketSize(maximumPacketSize);
if (roleIsServer) {
handshaker.setSNIMatchers(sniMatchers);
handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites);
@@ -2612,14 +2560,6 @@
}
/**
- * Returns a boolean indicating whether the ChangeCipherSpec message
- * has been received for this handshake.
- */
- boolean receivedChangeCipherSpec() {
- return receivedCCS;
- }
-
- /**
* Returns a printable representation of this end of the connection.
*/
@Override
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,441 @@
+/*
+ * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.*;
+import java.nio.*;
+
+import javax.crypto.BadPaddingException;
+
+import javax.net.ssl.*;
+
+import sun.misc.HexDumpEncoder;
+
+
+/**
+ * {@code InputRecord} implementation for {@code SSLSocket}.
+ *
+ * @author David Brownell
+ */
+final class SSLSocketInputRecord extends InputRecord implements SSLRecord {
+ private OutputStream deliverStream = null;
+ private byte[] temporary = new byte[1024];
+
+ // used by handshake hash computation for handshake fragment
+ private byte prevType = -1;
+ private int hsMsgOff = 0;
+ private int hsMsgLen = 0;
+
+ private boolean formatVerified = false; // SSLv2 ruled out?
+
+ private boolean hasHeader = false; // Had read the record header
+
+ SSLSocketInputRecord() {
+ this.readAuthenticator = MAC.TLS_NULL;
+ }
+
+ @Override
+ int bytesInCompletePacket(InputStream is) throws IOException {
+
+ if (!hasHeader) {
+ // read exactly one record
+ int really = read(is, temporary, 0, headerSize);
+ if (really < 0) {
+ throw new EOFException("SSL peer shut down incorrectly");
+ }
+ hasHeader = true;
+ }
+
+ byte byteZero = temporary[0];
+ int len = 0;
+
+ /*
+ * If we have already verified previous packets, we can
+ * ignore the verifications steps, and jump right to the
+ * determination. Otherwise, try one last hueristic to
+ * see if it's SSL/TLS.
+ */
+ if (formatVerified ||
+ (byteZero == ct_handshake) || (byteZero == ct_alert)) {
+ /*
+ * Last sanity check that it's not a wild record
+ */
+ ProtocolVersion recordVersion =
+ ProtocolVersion.valueOf(temporary[1], temporary[2]);
+
+ // check the record version
+ checkRecordVersion(recordVersion, false);
+
+ /*
+ * Reasonably sure this is a V3, disable further checks.
+ * We can't do the same in the v2 check below, because
+ * read still needs to parse/handle the v2 clientHello.
+ */
+ formatVerified = true;
+
+ /*
+ * One of the SSLv3/TLS message types.
+ */
+ len = ((temporary[3] & 0xFF) << 8) +
+ (temporary[4] & 0xFF) + headerSize;
+ } else {
+ /*
+ * Must be SSLv2 or something unknown.
+ * Check if it's short (2 bytes) or
+ * long (3) header.
+ *
+ * Internals can warn about unsupported SSLv2
+ */
+ boolean isShort = ((byteZero & 0x80) != 0);
+
+ if (isShort && ((temporary[2] == 1) || (temporary[2] == 4))) {
+ ProtocolVersion recordVersion =
+ ProtocolVersion.valueOf(temporary[3], temporary[4]);
+
+ // check the record version
+ checkRecordVersion(recordVersion, true);
+
+ /*
+ * Client or Server Hello
+ */
+ //
+ // Short header is using here. We reverse the code here
+ // in case it it used in the future.
+ //
+ // int mask = (isShort ? 0x7F : 0x3F);
+ // len = ((byteZero & mask) << 8) +
+ // (temporary[1] & 0xFF) + (isShort ? 2 : 3);
+ //
+ len = ((byteZero & 0x7F) << 8) + (temporary[1] & 0xFF) + 2;
+ } else {
+ // Gobblygook!
+ throw new SSLException(
+ "Unrecognized SSL message, plaintext connection?");
+ }
+ }
+
+ return len;
+ }
+
+ // destination.position() is zero.
+ @Override
+ Plaintext decode(InputStream is, ByteBuffer destination)
+ throws IOException, BadPaddingException {
+
+ if (isClosed) {
+ return null;
+ }
+
+ if (!hasHeader) {
+ // read exactly one record
+ int really = read(is, temporary, 0, headerSize);
+ if (really < 0) {
+ throw new EOFException("SSL peer shut down incorrectly");
+ }
+ hasHeader = true;
+ }
+
+ Plaintext plaintext = null;
+ if (!formatVerified) {
+ formatVerified = true;
+
+ /*
+ * The first record must either be a handshake record or an
+ * alert message. If it's not, it is either invalid or an
+ * SSLv2 message.
+ */
+ if ((temporary[0] != ct_handshake) &&
+ (temporary[0] != ct_alert)) {
+
+ plaintext = handleUnknownRecord(is, temporary, destination);
+ }
+ }
+
+ if (plaintext == null) {
+ plaintext = decodeInputRecord(is, temporary, destination);
+ }
+
+ // The record header should has comsumed.
+ hasHeader = false;
+
+ return plaintext;
+ }
+
+ @Override
+ void setDeliverStream(OutputStream outputStream) {
+ this.deliverStream = outputStream;
+ }
+
+ // Note that destination may be null
+ private Plaintext decodeInputRecord(InputStream is, byte[] header,
+ ByteBuffer destination) throws IOException, BadPaddingException {
+
+ byte contentType = header[0];
+ byte majorVersion = header[1];
+ byte minorVersion = header[2];
+ int contentLen = ((header[3] & 0xFF) << 8) + (header[4] & 0xFF);
+
+ //
+ // Check for upper bound.
+ //
+ // Note: May check packetSize limit in the future.
+ if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {
+ throw new SSLProtocolException(
+ "Bad input record size, TLSCiphertext.length = " + contentLen);
+ }
+
+ //
+ // Read a complete record.
+ //
+ if (destination == null) {
+ destination = ByteBuffer.allocate(headerSize + contentLen);
+ } // Otherwise, the destination buffer should have enough room.
+
+ int dstPos = destination.position();
+ destination.put(temporary, 0, headerSize);
+ while (contentLen > 0) {
+ int howmuch = Math.min(temporary.length, contentLen);
+ int really = read(is, temporary, 0, howmuch);
+ if (really < 0) {
+ throw new EOFException("SSL peer shut down incorrectly");
+ }
+
+ destination.put(temporary, 0, howmuch);
+ contentLen -= howmuch;
+ }
+ destination.flip();
+ destination.position(dstPos + headerSize);
+
+ if (debug != null && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", READ: " +
+ ProtocolVersion.valueOf(majorVersion, minorVersion) +
+ " " + Record.contentName(contentType) + ", length = " +
+ destination.remaining());
+ }
+
+ //
+ // Decrypt the fragment
+ //
+ ByteBuffer plaintext =
+ decrypt(readAuthenticator, readCipher, contentType, destination);
+
+ if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) {
+ throw new SSLProtocolException(
+ "Expected to get a handshake fragment");
+ }
+
+ //
+ // handshake hashing
+ //
+ if (contentType == ct_handshake) {
+ int pltPos = plaintext.position();
+ int pltLim = plaintext.limit();
+ int frgPos = pltPos;
+ for (int remains = plaintext.remaining(); remains > 0;) {
+ int howmuch;
+ byte handshakeType;
+ if (hsMsgOff < hsMsgLen) {
+ // a fragment of the handshake message
+ howmuch = Math.min((hsMsgLen - hsMsgOff), remains);
+ handshakeType = prevType;
+
+ hsMsgOff += howmuch;
+ if (hsMsgOff == hsMsgLen) {
+ // Now is a complete handshake message.
+ hsMsgOff = 0;
+ hsMsgLen = 0;
+ }
+ } else { // hsMsgOff == hsMsgLen, a new handshake message
+ handshakeType = plaintext.get();
+ int handshakeLen = ((plaintext.get() & 0xFF) << 16) |
+ ((plaintext.get() & 0xFF) << 8) |
+ (plaintext.get() & 0xFF);
+ plaintext.position(frgPos);
+ if (remains < (handshakeLen + 1)) { // 1: handshake type
+ // This handshake message is fragmented.
+ prevType = handshakeType;
+ hsMsgOff = remains - 4; // 4: handshake header
+ hsMsgLen = handshakeLen;
+ }
+
+ howmuch = Math.min(handshakeLen + 4, remains);
+ }
+
+ plaintext.limit(frgPos + howmuch);
+
+ if (handshakeType == HandshakeMessage.ht_hello_request) {
+ // omitted from handshake hash computation
+ } else if ((handshakeType != HandshakeMessage.ht_finished) &&
+ (handshakeType != HandshakeMessage.ht_certificate_verify)) {
+
+ if (handshakeHash == null) {
+ // used for cache only
+ handshakeHash = new HandshakeHash(false);
+ }
+ handshakeHash.update(plaintext);
+ } else {
+ // Reserve until this handshake message has been processed.
+ if (handshakeHash == null) {
+ // used for cache only
+ handshakeHash = new HandshakeHash(false);
+ }
+ handshakeHash.reserve(plaintext);
+ }
+
+ plaintext.position(frgPos + howmuch);
+ plaintext.limit(pltLim);
+
+ frgPos += howmuch;
+ remains -= howmuch;
+ }
+ plaintext.position(pltPos);
+ }
+
+ return new Plaintext(contentType,
+ majorVersion, minorVersion, -1, -1L, plaintext);
+ // recordEpoch, recordSeq, plaintext);
+ }
+
+ private Plaintext handleUnknownRecord(InputStream is, byte[] header,
+ ByteBuffer destination) throws IOException, BadPaddingException {
+
+ byte firstByte = header[0];
+ byte thirdByte = header[2];
+
+ // Does it look like a Version 2 client hello (V2ClientHello)?
+ if (((firstByte & 0x80) != 0) && (thirdByte == 1)) {
+ /*
+ * If SSLv2Hello is not enabled, throw an exception.
+ */
+ if (helloVersion != ProtocolVersion.SSL20Hello) {
+ throw new SSLHandshakeException("SSLv2Hello is not enabled");
+ }
+
+ byte majorVersion = header[3];
+ byte minorVersion = header[4];
+
+ if ((majorVersion == ProtocolVersion.SSL20Hello.major) &&
+ (minorVersion == ProtocolVersion.SSL20Hello.minor)) {
+
+ /*
+ * Looks like a V2 client hello, but not one saying
+ * "let's talk SSLv3". So we need to send an SSLv2
+ * error message, one that's treated as fatal by
+ * clients (Otherwise we'll hang.)
+ */
+ deliverStream.write(SSLRecord.v2NoCipher); // SSLv2Hello
+
+ if (debug != null) {
+ if (Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ "Requested to negotiate unsupported SSLv2!");
+ }
+
+ if (Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Raw write]: length = " +
+ SSLRecord.v2NoCipher.length,
+ SSLRecord.v2NoCipher);
+ }
+ }
+
+ throw new SSLException("Unsupported SSL v2.0 ClientHello");
+ }
+
+ int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF);
+
+ if (destination == null) {
+ destination = ByteBuffer.allocate(headerSize + msgLen);
+ }
+ destination.put(temporary, 0, headerSize);
+ msgLen -= 3; // had read 3 bytes of content as header
+ while (msgLen > 0) {
+ int howmuch = Math.min(temporary.length, msgLen);
+ int really = read(is, temporary, 0, howmuch);
+ if (really < 0) {
+ throw new EOFException("SSL peer shut down incorrectly");
+ }
+
+ destination.put(temporary, 0, howmuch);
+ msgLen -= howmuch;
+ }
+ destination.flip();
+
+ /*
+ * If we can map this into a V3 ClientHello, read and
+ * hash the rest of the V2 handshake, turn it into a
+ * V3 ClientHello message, and pass it up.
+ */
+ destination.position(2); // exclude the header
+
+ if (handshakeHash == null) {
+ // used for cache only
+ handshakeHash = new HandshakeHash(false);
+ }
+ handshakeHash.update(destination);
+ destination.position(0);
+
+ ByteBuffer converted = convertToClientHello(destination);
+
+ if (debug != null && Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Converted] ClientHello", converted);
+ }
+
+ return new Plaintext(ct_handshake,
+ majorVersion, minorVersion, -1, -1L, converted);
+ } else {
+ if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
+ throw new SSLException("SSL V2.0 servers are not supported.");
+ }
+
+ throw new SSLException("Unsupported or unrecognized SSL message");
+ }
+ }
+
+ // Read the exact bytes of data, otherwise, return -1.
+ private static int read(InputStream is,
+ byte[] buffer, int offset, int len) throws IOException {
+ int n = 0;
+ while (n < len) {
+ int readLen = is.read(buffer, offset + n, len - n);
+ if (readLen < 0) {
+ return -1;
+ }
+
+ if (debug != null && Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Raw read]: length = " + readLen,
+ buffer, offset + n, readLen);
+ }
+
+ n += readLen;
+ }
+
+ return n;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.*;
+import java.nio.*;
+import java.util.Arrays;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import sun.misc.HexDumpEncoder;
+
+
+/**
+ * {@code OutputRecord} implementation for {@code SSLSocket}.
+ */
+final class SSLSocketOutputRecord extends OutputRecord implements SSLRecord {
+ private OutputStream deliverStream = null;
+
+ SSLSocketOutputRecord() {
+ this.writeAuthenticator = MAC.TLS_NULL;
+
+ this.packetSize = SSLRecord.maxRecordSize;
+ this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
+ }
+
+ @Override
+ void encodeAlert(byte level, byte description) throws IOException {
+ // use the buf of ByteArrayOutputStream
+ int position = headerSize + writeCipher.getExplicitNonceSize();
+ count = position;
+
+ write(level);
+ write(description);
+
+ if (debug != null && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", WRITE: " + protocolVersion +
+ " " + Record.contentName(Record.ct_alert) +
+ ", length = " + (count - headerSize));
+ }
+
+ // Encrypt the fragment and wrap up a record.
+ encrypt(writeAuthenticator, writeCipher,
+ Record.ct_alert, headerSize);
+
+ // deliver this message
+ deliverStream.write(buf, 0, count); // may throw IOException
+ deliverStream.flush(); // may throw IOException
+
+ if (debug != null && Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Raw write]: length = " + count, buf, 0, count);
+ }
+
+ // reset the internal buffer
+ count = 0;
+ }
+
+ @Override
+ void encodeHandshake(byte[] source,
+ int offset, int length) throws IOException {
+
+ if (firstMessage) {
+ firstMessage = false;
+
+ if ((helloVersion == ProtocolVersion.SSL20Hello) &&
+ (source[offset] == HandshakeMessage.ht_client_hello) &&
+ // 5: recode header size
+ (source[offset + 4 + 2 + 32] == 0)) {
+ // V3 session ID is empty
+ // 4: handshake header size
+ // 2: client_version in ClientHello
+ // 32: random in ClientHello
+
+ ByteBuffer v2ClientHello = encodeV2ClientHello(
+ source, (offset + 4), (length - 4));
+
+ byte[] record = v2ClientHello.array(); // array offset is zero
+ int limit = v2ClientHello.limit();
+ handshakeHash.update(record, 2, (limit - 2));
+
+ if (debug != null && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", WRITE: SSLv2 ClientHello message" +
+ ", length = " + limit);
+ }
+
+ // deliver this message
+ //
+ // Version 2 ClientHello message should be plaintext.
+ //
+ // No max fragment length negotiation.
+ deliverStream.write(record, 0, limit);
+ deliverStream.flush();
+
+ if (debug != null && Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Raw write]: length = " + count, record, 0, limit);
+ }
+
+ return;
+ }
+ }
+
+ byte handshakeType = source[0];
+ if (handshakeType != HandshakeMessage.ht_hello_request) {
+ handshakeHash.update(source, offset, length);
+ }
+
+ int fragLimit = getFragLimit();
+ int position = headerSize + writeCipher.getExplicitNonceSize();
+ if (count == 0) {
+ count = position;
+ }
+
+ if ((count - position) < (fragLimit - length)) {
+ write(source, offset, length);
+ return;
+ }
+
+ for (int limit = (offset + length); offset < limit;) {
+
+ int remains = (limit - offset) + (count - position);
+ int fragLen = Math.min(fragLimit, remains);
+
+ // use the buf of ByteArrayOutputStream
+ write(source, offset, fragLen);
+ if (remains < fragLimit) {
+ return;
+ }
+
+ if (debug != null && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", WRITE: " + protocolVersion +
+ " " + Record.contentName(Record.ct_handshake) +
+ ", length = " + (count - headerSize));
+ }
+
+ // Encrypt the fragment and wrap up a record.
+ encrypt(writeAuthenticator, writeCipher,
+ Record.ct_handshake, headerSize);
+
+ // deliver this message
+ deliverStream.write(buf, 0, count); // may throw IOException
+ deliverStream.flush(); // may throw IOException
+
+ if (debug != null && Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Raw write]: length = " + count, buf, 0, count);
+ }
+
+ // reset the offset
+ offset += fragLen;
+
+ // reset the internal buffer
+ count = position;
+ }
+ }
+
+ @Override
+ void encodeChangeCipherSpec() throws IOException {
+
+ // use the buf of ByteArrayOutputStream
+ int position = headerSize + writeCipher.getExplicitNonceSize();
+ count = position;
+
+ write((byte)1); // byte 1: change_cipher_spec(
+
+ if (debug != null && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", WRITE: " + protocolVersion +
+ " " + Record.contentName(Record.ct_change_cipher_spec) +
+ ", length = " + (count - headerSize));
+ }
+
+ // Encrypt the fragment and wrap up a record.
+ encrypt(writeAuthenticator, writeCipher,
+ Record.ct_change_cipher_spec, headerSize);
+
+ // deliver this message
+ deliverStream.write(buf, 0, count); // may throw IOException
+ // deliverStream.flush(); // flush in Finished
+
+ if (debug != null && Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Raw write]: length = " + count, buf, 0, count);
+ }
+
+ // reset the internal buffer
+ count = 0;
+ }
+
+ @Override
+ public void flush() throws IOException {
+ int position = headerSize + writeCipher.getExplicitNonceSize();
+ if (count <= position) {
+ return;
+ }
+
+ if (debug != null && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", WRITE: " + protocolVersion +
+ " " + Record.contentName(Record.ct_handshake) +
+ ", length = " + (count - headerSize));
+ }
+
+ // Encrypt the fragment and wrap up a record.
+ encrypt(writeAuthenticator, writeCipher,
+ Record.ct_handshake, headerSize);
+
+ // deliver this message
+ deliverStream.write(buf, 0, count); // may throw IOException
+ deliverStream.flush(); // may throw IOException
+
+ if (debug != null && Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Raw write]: length = " + count, buf, 0, count);
+ }
+
+ // reset the internal buffer
+ count = 0; // DON'T use position
+ }
+
+ @Override
+ void deliver(byte[] source, int offset, int length) throws IOException {
+
+ if (writeAuthenticator.seqNumOverflow()) {
+ if (debug != null && Debug.isOn("ssl")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", sequence number extremely close to overflow " +
+ "(2^64-1 packets). Closing connection.");
+ }
+
+ throw new SSLHandshakeException("sequence number overflow");
+ }
+
+ boolean isFirstRecordOfThePayload = true;
+ for (int limit = (offset + length); offset < limit;) {
+ int macLen = 0;
+ if (writeAuthenticator instanceof MAC) {
+ macLen = ((MAC)writeAuthenticator).MAClen();
+ }
+
+ int fragLen;
+ if (packetSize > 0) {
+ fragLen = Math.min(maxRecordSize, packetSize);
+ fragLen = writeCipher.calculateFragmentSize(
+ fragLen, macLen, headerSize);
+
+ fragLen = Math.min(fragLen, Record.maxDataSize);
+ } else {
+ fragLen = Record.maxDataSize;
+ }
+
+ if (fragmentSize > 0) {
+ fragLen = Math.min(fragLen, fragmentSize);
+ }
+
+ if (isFirstRecordOfThePayload && needToSplitPayload()) {
+ fragLen = 1;
+ isFirstRecordOfThePayload = false;
+ } else {
+ fragLen = Math.min(fragLen, (limit - offset));
+ }
+
+ // use the buf of ByteArrayOutputStream
+ int position = headerSize + writeCipher.getExplicitNonceSize();
+ count = position;
+ write(source, offset, fragLen);
+
+ if (debug != null && Debug.isOn("record")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", WRITE: " + protocolVersion +
+ " " + Record.contentName(Record.ct_application_data) +
+ ", length = " + (count - headerSize));
+ }
+
+ // Encrypt the fragment and wrap up a record.
+ encrypt(writeAuthenticator, writeCipher,
+ Record.ct_application_data, headerSize);
+
+ // deliver this message
+ deliverStream.write(buf, 0, count); // may throw IOException
+ deliverStream.flush(); // may throw IOException
+
+ if (debug != null && Debug.isOn("packet")) {
+ Debug.printHex(
+ "[Raw write]: length = " + count, buf, 0, count);
+ }
+
+ // reset the internal buffer
+ count = 0;
+
+ if (isFirstAppOutputRecord) {
+ isFirstAppOutputRecord = false;
+ }
+
+ offset += fragLen;
+ }
+ }
+
+ @Override
+ void setDeliverStream(OutputStream outputStream) {
+ this.deliverStream = outputStream;
+ }
+
+ /*
+ * Need to split the payload except the following cases:
+ *
+ * 1. protocol version is TLS 1.1 or later;
+ * 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
+ * 3. the payload is the first application record of a freshly
+ * negotiated TLS session.
+ * 4. the CBC protection is disabled;
+ *
+ * By default, we counter chosen plaintext issues on CBC mode
+ * ciphersuites in SSLv3/TLS1.0 by sending one byte of application
+ * data in the first record of every payload, and the rest in
+ * subsequent record(s). Note that the issues have been solved in
+ * TLS 1.1 or later.
+ *
+ * It is not necessary to split the very first application record of
+ * a freshly negotiated TLS session, as there is no previous
+ * application data to guess. To improve compatibility, we will not
+ * split such records.
+ *
+ * This avoids issues in the outbound direction. For a full fix,
+ * the peer must have similar protections.
+ */
+ boolean needToSplitPayload() {
+ return (!protocolVersion.useTLS11PlusSpec()) &&
+ writeCipher.isCBCMode() && !isFirstAppOutputRecord &&
+ Record.enableCBCProtection;
+ }
+
+ private int getFragLimit() {
+ int macLen = 0;
+ if (writeAuthenticator instanceof MAC) {
+ macLen = ((MAC)writeAuthenticator).MAClen();
+ }
+
+ int fragLimit;
+ if (packetSize > 0) {
+ fragLimit = Math.min(maxRecordSize, packetSize);
+ fragLimit = writeCipher.calculateFragmentSize(
+ fragLimit, macLen, headerSize);
+
+ fragLimit = Math.min(fragLimit, Record.maxDataSize);
+ } else {
+ fragLimit = Record.maxDataSize;
+ }
+
+ if (fragmentSize > 0) {
+ fragLimit = Math.min(fragLimit, fragmentSize);
+ }
+
+ return fragLimit;
+ }
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Thu Jun 04 18:49:37 2015 -0700
@@ -38,8 +38,6 @@
import javax.net.ssl.*;
-import javax.security.auth.Subject;
-
import sun.security.util.KeyUtil;
import sun.security.action.GetPropertyAction;
import sun.security.ssl.HandshakeMessage.*;
@@ -58,7 +56,7 @@
final class ServerHandshaker extends Handshaker {
// is the server going to require the client to authenticate?
- private byte doClientAuth;
+ private ClientAuthType doClientAuth;
// our authentication info
private X509Certificate[] certs;
@@ -143,13 +141,13 @@
* Constructor ... use the keys found in the auth context.
*/
ServerHandshaker(SSLSocketImpl socket, SSLContextImpl context,
- ProtocolList enabledProtocols, byte clientAuth,
+ ProtocolList enabledProtocols, ClientAuthType clientAuth,
ProtocolVersion activeProtocolVersion, boolean isInitialHandshake,
boolean secureRenegotiation,
byte[] clientVerifyData, byte[] serverVerifyData) {
super(socket, context, enabledProtocols,
- (clientAuth != SSLEngineImpl.clauth_none), false,
+ (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
activeProtocolVersion, isInitialHandshake, secureRenegotiation,
clientVerifyData, serverVerifyData);
doClientAuth = clientAuth;
@@ -159,15 +157,16 @@
* Constructor ... use the keys found in the auth context.
*/
ServerHandshaker(SSLEngineImpl engine, SSLContextImpl context,
- ProtocolList enabledProtocols, byte clientAuth,
+ ProtocolList enabledProtocols, ClientAuthType clientAuth,
ProtocolVersion activeProtocolVersion,
boolean isInitialHandshake, boolean secureRenegotiation,
- byte[] clientVerifyData, byte[] serverVerifyData) {
+ byte[] clientVerifyData, byte[] serverVerifyData,
+ boolean isDTLS) {
super(engine, context, enabledProtocols,
- (clientAuth != SSLEngineImpl.clauth_none), false,
+ (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
activeProtocolVersion, isInitialHandshake, secureRenegotiation,
- clientVerifyData, serverVerifyData);
+ clientVerifyData, serverVerifyData, isDTLS);
doClientAuth = clientAuth;
}
@@ -176,7 +175,7 @@
* whether client authentication is required. Otherwise,
* we will need to wait for the next handshake.
*/
- void setClientAuth(byte clientAuth) {
+ void setClientAuth(ClientAuthType clientAuth) {
doClientAuth = clientAuth;
}
@@ -192,21 +191,15 @@
@Override
void processMessage(byte type, int message_len)
throws IOException {
- //
- // In SSLv3 and TLS, messages follow strictly increasing
- // numerical order _except_ for one annoying special case.
- //
- if ((state >= type)
- && (state != HandshakeMessage.ht_client_key_exchange
- && type != HandshakeMessage.ht_certificate_verify)) {
- throw new SSLProtocolException(
- "Handshake message sequence violation, state = " + state
- + ", type = " + type);
- }
+
+ // check the handshake state
+ handshakeState.check(type);
switch (type) {
case HandshakeMessage.ht_client_hello:
- ClientHello ch = new ClientHello(input, message_len);
+ ClientHello ch = new ClientHello(input, message_len, isDTLS);
+ handshakeState.update(ch, resumingSession);
+
/*
* send it off for processing.
*/
@@ -214,12 +207,14 @@
break;
case HandshakeMessage.ht_certificate:
- if (doClientAuth == SSLEngineImpl.clauth_none) {
+ if (doClientAuth == ClientAuthType.CLIENT_AUTH_NONE) {
fatalSE(Alerts.alert_unexpected_message,
"client sent unsolicited cert chain");
// NOTREACHED
}
- this.clientCertificate(new CertificateMsg(input));
+ CertificateMsg certificateMsg = new CertificateMsg(input);
+ handshakeState.update(certificateMsg, resumingSession);
+ this.clientCertificate(certificateMsg);
break;
case HandshakeMessage.ht_client_key_exchange:
@@ -237,18 +232,9 @@
protocolVersion, clientRequestedVersion,
sslContext.getSecureRandom(), input,
message_len, privateKey);
+ handshakeState.update(pms, resumingSession);
preMasterSecret = this.clientKeyExchange(pms);
break;
- case K_KRB5:
- case K_KRB5_EXPORT:
- preMasterSecret = this.clientKeyExchange(
- new KerberosClientKeyExchange(protocolVersion,
- clientRequestedVersion,
- sslContext.getSecureRandom(),
- input,
- this.getAccSE(),
- serviceCreds));
- break;
case K_DHE_RSA:
case K_DHE_DSS:
case K_DH_ANON:
@@ -258,20 +244,39 @@
* protocol difference in these five flavors is in how
* the ServerKeyExchange message was constructed!
*/
- preMasterSecret = this.clientKeyExchange(
- new DHClientKeyExchange(input));
+ DHClientKeyExchange dhcke = new DHClientKeyExchange(input);
+ handshakeState.update(dhcke, resumingSession);
+ preMasterSecret = this.clientKeyExchange(dhcke);
break;
case K_ECDH_RSA:
case K_ECDH_ECDSA:
case K_ECDHE_RSA:
case K_ECDHE_ECDSA:
case K_ECDH_ANON:
- preMasterSecret = this.clientKeyExchange
- (new ECDHClientKeyExchange(input));
+ ECDHClientKeyExchange ecdhcke =
+ new ECDHClientKeyExchange(input);
+ handshakeState.update(ecdhcke, resumingSession);
+ preMasterSecret = this.clientKeyExchange(ecdhcke);
break;
default:
- throw new SSLProtocolException
- ("Unrecognized key exchange: " + keyExchange);
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find(keyExchange.name);
+ if (p == null) {
+ throw new SSLProtocolException
+ ("Unrecognized key exchange: " + keyExchange);
+ }
+ byte[] encodedTicket = input.getBytes16();
+ input.getBytes16();
+ byte[] secret = input.getBytes16();
+ ClientKeyExchange cke = p.createServerExchange(protocolVersion,
+ clientRequestedVersion,
+ sslContext.getSecureRandom(),
+ encodedTicket,
+ secret,
+ this.getAccSE(), serviceCreds);
+ handshakeState.update(cke, resumingSession);
+ preMasterSecret = this.clientKeyExchange(cke);
+ break;
}
//
@@ -282,20 +287,20 @@
break;
case HandshakeMessage.ht_certificate_verify:
- this.clientCertificateVerify(new CertificateVerify(input,
- localSupportedSignAlgs, protocolVersion));
+ CertificateVerify cvm =
+ new CertificateVerify(input,
+ localSupportedSignAlgs, protocolVersion);
+ handshakeState.update(cvm, resumingSession);
+ this.clientCertificateVerify(cvm);
+
break;
case HandshakeMessage.ht_finished:
- // A ChangeCipherSpec record must have been received prior to
- // reception of the Finished message (RFC 5246, 7.4.9).
- if (!receivedChangeCipherSpec()) {
- fatalSE(Alerts.alert_handshake_failure,
- "Received Finished message before ChangeCipherSpec");
- }
+ Finished cfm =
+ new Finished(protocolVersion, input, cipherSuite);
+ handshakeState.update(cfm, resumingSession);
+ this.clientFinished(cfm);
- this.clientFinished(
- new Finished(protocolVersion, input, cipherSuite));
break;
default:
@@ -303,17 +308,6 @@
"Illegal server handshake msg, " + type);
}
- //
- // Move state machine forward if the message handling
- // code didn't already do so
- //
- if (state < type) {
- if(type == HandshakeMessage.ht_certificate_verify) {
- state = type + 2; // an annoying special case
- } else {
- state = type;
- }
- }
}
@@ -344,7 +338,7 @@
//
// This will not have any impact on server initiated renegotiation.
if (rejectClientInitiatedRenego && !isInitialHandshake &&
- state != HandshakeMessage.ht_hello_request) {
+ !serverHelloRequested) {
fatalSE(Alerts.alert_handshake_failure,
"Client initiated renegotiation is not allowed");
}
@@ -438,7 +432,7 @@
}
} else if (!allowUnsafeRenegotiation) {
// abort the handshake
- if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) {
+ if (activeProtocolVersion.useTLS10PlusSpec()) {
// respond with a no_renegotiation warning
warningSE(Alerts.alert_no_renegotiation);
@@ -480,11 +474,52 @@
}
}
- /*
- * Always make sure this entire record has been digested before we
- * start emitting output, to ensure correct digesting order.
- */
- input.digestNow();
+ // check the "max_fragment_length" extension
+ MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension)
+ mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
+ if ((maxFragLenExt != null) && (maximumPacketSize != 0)) {
+ // Not yet consider the impact of IV/MAC/padding.
+ int estimatedMaxFragSize = maximumPacketSize;
+ if (isDTLS) {
+ estimatedMaxFragSize -= DTLSRecord.headerSize;
+ } else {
+ estimatedMaxFragSize -= SSLRecord.headerSize;
+ }
+
+ if (maxFragLenExt.getMaxFragLen() > estimatedMaxFragSize) {
+ // For better interoperability, abort the maximum fragment
+ // length negotiation, rather than terminate the connection
+ // with a fatal alert.
+ maxFragLenExt = null;
+
+ // fatalSE(Alerts.alert_illegal_parameter,
+ // "Not an allowed max_fragment_length value");
+ }
+ }
+
+ // cookie exchange
+ if (isDTLS) {
+ HelloCookieManager hcMgr = sslContext.getHelloCookieManager();
+ if ((mesg.cookie == null) || (mesg.cookie.length == 0) ||
+ (!hcMgr.isValid(mesg))) {
+
+ //
+ // Perform cookie exchange for DTLS handshaking if no cookie
+ // or the cookie is invalid in the ClientHello message.
+ //
+ HelloVerifyRequest m0 = new HelloVerifyRequest(hcMgr, mesg);
+
+ if (debug != null && Debug.isOn("handshake")) {
+ m0.print(System.out);
+ }
+
+ m0.write(output);
+ handshakeState.update(m0, resumingSession);
+ output.flush();
+
+ return;
+ }
+ }
/*
* FIRST, construct the ServerHello using the options and priorities
@@ -580,7 +615,7 @@
}
if (resumingSession &&
- (doClientAuth == SSLEngineImpl.clauth_required)) {
+ (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED)) {
try {
previous.getPeerPrincipal();
} catch (SSLPeerUnverifiedException e) {
@@ -591,47 +626,21 @@
// validate subject identity
if (resumingSession) {
CipherSuite suite = previous.getSuite();
- if (suite.keyExchange == K_KRB5 ||
- suite.keyExchange == K_KRB5_EXPORT) {
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find(suite.keyExchange.name);
+ if (p != null) {
Principal localPrincipal = previous.getLocalPrincipal();
- Subject subject = null;
- try {
- subject = AccessController.doPrivileged(
- new PrivilegedExceptionAction<Subject>() {
- @Override
- public Subject run() throws Exception {
- return
- Krb5Helper.getServerSubject(getAccSE());
- }});
- } catch (PrivilegedActionException e) {
- subject = null;
- if (debug != null && Debug.isOn("session")) {
- System.out.println("Attempt to obtain" +
- " subject failed!");
- }
- }
-
- if (subject != null) {
- // Eliminate dependency on KerberosPrincipal
- if (Krb5Helper.isRelated(subject, localPrincipal)) {
- if (debug != null && Debug.isOn("session"))
- System.out.println("Subject can" +
- " provide creds for princ");
- } else {
- resumingSession = false;
- if (debug != null && Debug.isOn("session"))
- System.out.println("Subject cannot" +
- " provide creds for princ");
- }
+ if (p.isRelated(
+ false, getAccSE(), localPrincipal)) {
+ if (debug != null && Debug.isOn("session"))
+ System.out.println("Subject can" +
+ " provide creds for princ");
} else {
resumingSession = false;
if (debug != null && Debug.isOn("session"))
- System.out.println("Kerberos credentials are" +
- " not present in the current Subject;" +
- " check if " +
- " javax.security.auth.useSubjectAsCreds" +
- " system property has been set to false");
+ System.out.println("Subject cannot" +
+ " provide creds for princ");
}
}
}
@@ -660,7 +669,7 @@
}
}
}
- } // else client did not try to resume
+ } // else client did not try to resume
//
// If client hasn't specified a session we can resume, start a
@@ -677,7 +686,7 @@
// We only need to handle the "signature_algorithm" extension
// for full handshakes and TLS 1.2 or later.
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
SignatureAlgorithmsExtension signAlgs =
(SignatureAlgorithmsExtension)mesg.extensions.get(
ExtensionType.EXT_SIGNATURE_ALGORITHMS);
@@ -708,7 +717,7 @@
sslContext.getSecureRandom(),
getHostAddressSE(), getPortSE());
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
if (peerSupportedSignAlgs != null) {
session.setPeerSupportedSignatureAlgorithms(
peerSupportedSignAlgs);
@@ -734,12 +743,41 @@
session.setLocalPrivateKey(privateKey);
// chooseCompression(mesg);
+
+ // set the negotiated maximum fragment in the session
+ //
+ // The protocol version and cipher suite have been negotiated
+ // in previous processes.
+ if (maxFragLenExt != null) {
+ int maxFragLen = maxFragLenExt.getMaxFragLen();
+
+ // More check of the requested "max_fragment_length" extension.
+ if (maximumPacketSize != 0) {
+ int estimatedMaxFragSize = cipherSuite.calculatePacketSize(
+ maxFragLen, protocolVersion, isDTLS);
+ if (estimatedMaxFragSize > maximumPacketSize) {
+ // For better interoperability, abort the maximum
+ // fragment length negotiation, rather than terminate
+ // the connection with a fatal alert.
+ maxFragLenExt = null;
+
+ // fatalSE(Alerts.alert_illegal_parameter,
+ // "Not an allowed max_fragment_length value");
+ }
+ }
+
+ if (maxFragLenExt != null) {
+ session.setNegotiatedMaxFragSize(maxFragLen);
+ }
+ }
+
+ session.setMaximumPacketSize(maximumPacketSize);
} else {
// set the handshake session
setHandshakeSessionSE(session);
}
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());
}
@@ -771,11 +809,20 @@
}
}
+ if ((maxFragLenExt != null) && !resumingSession) {
+ // When resuming a session, the server MUST NOT include a
+ // max_fragment_length extension in the server hello.
+ //
+ // Otherwise, use the same value as the requested extension.
+ m1.extensions.add(maxFragLenExt);
+ }
+
if (debug != null && Debug.isOn("handshake")) {
m1.print(System.out);
System.out.println("Cipher suite: " + session.getSuite());
}
m1.write(output);
+ handshakeState.update(m1, resumingSession);
//
// If we are resuming a session, we finish writing handshake
@@ -784,6 +831,10 @@
if (resumingSession) {
calculateConnectionKeys(session.getMasterSecret());
sendChangeCipherAndFinish(false);
+
+ // expecting the final ChangeCipherSpec and Finished messages
+ expectingFinishFlightSE();
+
return;
}
@@ -796,9 +847,8 @@
* defined in the protocol spec are explicitly stated to require
* using RSA certificates.
*/
- if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
- // Server certificates are omitted for Kerberos ciphers
-
+ if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
+ // No external key exchange provider needs a cert now.
} else if ((keyExchange != K_DH_ANON) && (keyExchange != K_ECDH_ANON)) {
if (certs == null) {
throw new RuntimeException("no certificates");
@@ -815,6 +865,7 @@
m2.print(System.out);
}
m2.write(output);
+ handshakeState.update(m2, resumingSession);
// XXX has some side effects with OS TCP buffering,
// leave it out for now
@@ -839,9 +890,7 @@
ServerKeyExchange m3;
switch (keyExchange) {
case K_RSA:
- case K_KRB5:
- case K_KRB5_EXPORT:
- // no server key exchange for RSA or KRB5 ciphersuites
+ // no server key exchange for RSA ciphersuites
m3 = null;
break;
case K_RSA_EXPORT:
@@ -853,9 +902,9 @@
sslContext.getSecureRandom());
privateKey = tempPrivateKey;
} catch (GeneralSecurityException e) {
- throwSSLException
- ("Error generating RSA server key exchange", e);
m3 = null; // make compiler happy
+ throw new SSLException(
+ "Error generating RSA server key exchange", e);
}
} else {
// RSA_EXPORT with short key, don't need ServerKeyExchange
@@ -873,8 +922,9 @@
preferableSignatureAlgorithm,
protocolVersion);
} catch (GeneralSecurityException e) {
- throwSSLException("Error generating DH server key exchange", e);
m3 = null; // make compiler happy
+ throw new SSLException(
+ "Error generating DH server key exchange", e);
}
break;
case K_DH_ANON:
@@ -892,9 +942,9 @@
preferableSignatureAlgorithm,
protocolVersion);
} catch (GeneralSecurityException e) {
- throwSSLException(
- "Error generating ECDH server key exchange", e);
m3 = null; // make compiler happy
+ throw new SSLException(
+ "Error generating ECDH server key exchange", e);
}
break;
case K_ECDH_RSA:
@@ -903,6 +953,13 @@
m3 = null;
break;
default:
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find(keyExchange.name);
+ if (p != null) {
+ // No external key exchange provider needs a cert now.
+ m3 = null;
+ break;
+ }
throw new RuntimeException("internal error: " + keyExchange);
}
if (m3 != null) {
@@ -910,6 +967,7 @@
m3.print(System.out);
}
m3.write(output);
+ handshakeState.update(m3, resumingSession);
}
//
@@ -922,16 +980,16 @@
// Needed only if server requires client to authenticate self.
// Illegal for anonymous flavors, so we need to check that.
//
- // CertificateRequest is omitted for Kerberos ciphers
- if (doClientAuth != SSLEngineImpl.clauth_none &&
+ // No external key exchange provider needs a cert now.
+ if (doClientAuth != ClientAuthType.CLIENT_AUTH_NONE &&
keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON &&
- keyExchange != K_KRB5 && keyExchange != K_KRB5_EXPORT) {
+ ClientKeyExchangeService.find(keyExchange.name) == null) {
CertificateRequest m4;
X509Certificate caCerts[];
Collection<SignatureAndHashAlgorithm> localSignAlgs = null;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
// We currently use all local upported signature and hash
// algorithms. However, to minimize the computation cost
// of requested hash algorithms, we may use a restricted
@@ -959,6 +1017,7 @@
m4.print(System.out);
}
m4.write(output);
+ handshakeState.update(m4, resumingSession);
}
/*
@@ -970,6 +1029,7 @@
m5.print(System.out);
}
m5.write(output);
+ handshakeState.update(m5, resumingSession);
/*
* Flush any buffered messages so the client will see them.
@@ -1000,7 +1060,7 @@
continue;
}
- if (doClientAuth == SSLEngineImpl.clauth_required) {
+ if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
if ((suite.keyExchange == K_DH_ANON) ||
(suite.keyExchange == K_ECDH_ANON)) {
continue;
@@ -1043,12 +1103,12 @@
}
// must not negotiate the obsoleted weak cipher suites.
- if (protocolVersion.v >= suite.obsoleted) {
+ if (protocolVersion.obsoletes(suite)) {
return false;
}
// must not negotiate unsupported cipher suites.
- if (protocolVersion.v < suite.supported) {
+ if (!protocolVersion.supports(suite)) {
return false;
}
@@ -1062,7 +1122,7 @@
tempPublicKey = null;
Collection<SignatureAndHashAlgorithm> supportedSignAlgs = null;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
if (peerSupportedSignAlgs != null) {
supportedSignAlgs = peerSupportedSignAlgs;
} else {
@@ -1151,7 +1211,7 @@
}
// get preferable peer signature algorithm for server key exchange
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm(
supportedSignAlgs, "RSA", privateKey);
@@ -1169,7 +1229,7 @@
}
// get preferable peer signature algorithm for server key exchange
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm(
supportedSignAlgs, "RSA", privateKey);
@@ -1184,7 +1244,7 @@
break;
case K_DHE_DSS:
// get preferable peer signature algorithm for server key exchange
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm(
supportedSignAlgs, "DSA");
@@ -1202,7 +1262,7 @@
break;
case K_ECDHE_ECDSA:
// get preferable peer signature algorithm for server key exchange
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm(
supportedSignAlgs, "ECDSA");
@@ -1233,13 +1293,6 @@
}
setupStaticECDHKeys();
break;
- case K_KRB5:
- case K_KRB5_EXPORT:
- // need Kerberos Key
- if (!setupKerberosKeys()) {
- return false;
- }
- break;
case K_DH_ANON:
// no certs needed for anonymous
setupEphemeralDHKeys(suite.exportable, null);
@@ -1251,13 +1304,31 @@
}
break;
default:
- // internal error, unknown key exchange
- throw new RuntimeException("Unrecognized cipherSuite: " + suite);
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find(keyExchange.name);
+ if (p == null) {
+ // internal error, unknown key exchange
+ throw new RuntimeException("Unrecognized cipherSuite: " + suite);
+ }
+ // need service creds
+ if (serviceCreds == null) {
+ AccessControlContext acc = getAccSE();
+ serviceCreds = p.getServiceCreds(acc);
+ if (serviceCreds != null) {
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Using serviceCreds");
+ }
+ }
+ if (serviceCreds == null) {
+ return false;
+ }
+ }
+ break;
}
setCipherSuite(suite);
// set the peer implicit supported signature algorithms
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
if (peerSupportedSignAlgs == null) {
setPeerSupportedSignAlgs(supportedSignAlgs);
// we had alreay update the session
@@ -1442,73 +1513,10 @@
return true;
}
- /**
- * Retrieve the Kerberos key for the specified server principal
- * from the JAAS configuration file.
- *
- * @return true if successful, false if not available or invalid
+ /*
+ * Returns premaster secret for external key exchange services.
*/
- private boolean setupKerberosKeys() {
- if (serviceCreds != null) {
- return true;
- }
- try {
- final AccessControlContext acc = getAccSE();
- serviceCreds = AccessController.doPrivileged(
- // Eliminate dependency on KerberosKey
- new PrivilegedExceptionAction<Object>() {
- @Override
- public Object run() throws Exception {
- // get kerberos key for the default principal
- return Krb5Helper.getServiceCreds(acc);
- }});
-
- // check permission to access and use the secret key of the
- // Kerberized "host" service
- if (serviceCreds != null) {
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Using Kerberos creds");
- }
- String serverPrincipal =
- Krb5Helper.getServerPrincipalName(serviceCreds);
- if (serverPrincipal != null) {
- // When service is bound, we check ASAP. Otherwise,
- // will check after client request is received
- // in Kerberos ClientKeyExchange
- SecurityManager sm = System.getSecurityManager();
- try {
- if (sm != null) {
- // Eliminate dependency on ServicePermission
- sm.checkPermission(Krb5Helper.getServicePermission(
- serverPrincipal, "accept"), acc);
- }
- } catch (SecurityException se) {
- serviceCreds = null;
- // Do not destroy keys. Will affect Subject
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Permission to access Kerberos"
- + " secret key denied");
- }
- return false;
- }
- }
- }
- return serviceCreds != null;
- } catch (PrivilegedActionException e) {
- // Likely exception here is LoginExceptin
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Attempt to obtain Kerberos key failed: "
- + e.toString());
- }
- return false;
- }
- }
-
- /*
- * For Kerberos ciphers, the premaster secret is encrypted using
- * the session key. See RFC 2712.
- */
- private SecretKey clientKeyExchange(KerberosClientKeyExchange mesg)
+ private SecretKey clientKeyExchange(ClientKeyExchange mesg)
throws IOException {
if (debug != null && Debug.isOn("handshake")) {
@@ -1519,8 +1527,7 @@
session.setPeerPrincipal(mesg.getPeerPrincipal());
session.setLocalPrincipal(mesg.getLocalPrincipal());
- byte[] b = mesg.getUnencryptedPreMasterSecret();
- return new SecretKeySpec(b, "TlsPremasterSecret");
+ return mesg.clientKeyExchange();
}
/*
@@ -1571,7 +1578,7 @@
mesg.print(System.out);
}
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
SignatureAndHashAlgorithm signAlg =
mesg.getPreferableSignatureAlgorithm();
if (signAlg == null) {
@@ -1623,9 +1630,9 @@
* Verify if client did send the certificate when client
* authentication was required, otherwise server should not proceed
*/
- if (doClientAuth == SSLEngineImpl.clauth_required) {
+ if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
// get X500Principal of the end-entity certificate for X509-based
- // ciphersuites, or Kerberos principal for Kerberos ciphersuites
+ // ciphersuites, or Kerberos principal for Kerberos ciphersuites, etc
session.getPeerPrincipal();
}
@@ -1664,8 +1671,9 @@
* the change_cipher_spec and Finished message.
*/
if (!resumingSession) {
- input.digestNow();
sendChangeCipherAndFinish(true);
+ } else {
+ handshakeFinished = true;
}
/*
@@ -1695,7 +1703,8 @@
private void sendChangeCipherAndFinish(boolean finishedTag)
throws IOException {
- output.flush();
+ // Reload if this message has been reserved.
+ handshakeHash.reload();
Finished mesg = new Finished(protocolVersion, handshakeHash,
Finished.SERVER, session.getMasterSecret(), cipherSuite);
@@ -1713,16 +1722,6 @@
if (secureRenegotiation) {
serverVerifyData = mesg.getVerifyData();
}
-
- /*
- * Update state machine so client MUST send 'finished' next
- * The update should only take place if it is not in the fast
- * handshake mode since the server has to wait for a finished
- * message from the client.
- */
- if (finishedTag) {
- state = HandshakeMessage.ht_finished;
- }
}
@@ -1757,7 +1756,7 @@
* session will get an SSLPeerUnverifiedException.
*/
if ((description == Alerts.alert_no_certificate) &&
- (doClientAuth == SSLEngineImpl.clauth_requested)) {
+ (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED)) {
return;
}
@@ -1798,7 +1797,7 @@
* If the client authentication is only *REQUESTED* (e.g.
* not *REQUIRED*, this is an acceptable condition.)
*/
- if (doClientAuth == SSLEngineImpl.clauth_requested) {
+ if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED) {
return;
} else {
fatalSE(Alerts.alert_bad_certificate,
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SunJSSE.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SunJSSE.java Thu Jun 04 18:49:37 2015 -0700
@@ -61,7 +61,7 @@
private static String info = "Sun JSSE provider" +
"(PKCS12, SunX509/PKIX key/trust factories, " +
- "SSLv3/TLSv1/TLSv1.1/TLSv1.2)";
+ "SSLv3/TLSv1/TLSv1.1/TLSv1.2/DTLSv1.0/DTLSv1.2)";
private static String fipsInfo =
"Sun JSSE provider (FIPS mode, crypto provider ";
@@ -220,6 +220,13 @@
put("Alg.Alias.SSLContext.SSLv3", "TLSv1");
}
+ put("SSLContext.DTLSv1.0",
+ "sun.security.ssl.SSLContextImpl$DTLS10Context");
+ put("SSLContext.DTLSv1.2",
+ "sun.security.ssl.SSLContextImpl$DTLS12Context");
+ put("SSLContext.DTLS",
+ "sun.security.ssl.SSLContextImpl$DTLSContext");
+
put("SSLContext.Default",
"sun.security.ssl.SSLContextImpl$DefaultSSLContext");
--- a/jdk/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java Thu Jun 04 18:49:37 2015 -0700
@@ -190,7 +190,7 @@
if (session != null) {
ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol());
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
String[] peerSupportedSignAlgs = null;
if (session instanceof ExtendedSSLSession) {
@@ -218,7 +218,7 @@
if (session != null) {
ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol());
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
String[] peerSupportedSignAlgs = null;
if (session instanceof ExtendedSSLSession) {
--- a/jdk/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java Thu Jun 04 18:49:37 2015 -0700
@@ -204,7 +204,7 @@
// create the algorithm constraints
ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol());
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
if (session instanceof ExtendedSSLSession) {
ExtendedSSLSession extSession =
(ExtendedSSLSession)session;
@@ -256,7 +256,7 @@
// create the algorithm constraints
ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol());
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
if (session instanceof ExtendedSSLSession) {
ExtendedSSLSession extSession =
(ExtendedSSLSession)session;
--- a/jdk/src/java.base/share/classes/sun/security/util/HostnameChecker.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/util/HostnameChecker.java Thu Jun 04 18:49:37 2015 -0700
@@ -35,7 +35,7 @@
import javax.security.auth.x500.X500Principal;
-import sun.security.ssl.Krb5Helper;
+import sun.security.ssl.ClientKeyExchangeService;
import sun.security.x509.X500Name;
import sun.net.util.IPAddressUtil;
@@ -108,7 +108,12 @@
* Return the Server name from Kerberos principal.
*/
public static String getServerName(Principal principal) {
- return Krb5Helper.getPrincipalHostName(principal);
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find("KRB5");
+ if (p == null) {
+ throw new AssertionError("Kerberos should have been available");
+ }
+ return p.getServiceHostName(principal);
}
/**
--- a/jdk/src/java.base/share/conf/security/java.security Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/share/conf/security/java.security Thu Jun 04 18:49:37 2015 -0700
@@ -511,11 +511,11 @@
jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024
# Algorithm restrictions for Secure Socket Layer/Transport Layer Security
-# (SSL/TLS) processing
+# (SSL/TLS/DTLS) processing
#
# In some environments, certain algorithms or key lengths may be undesirable
-# when using SSL/TLS. This section describes the mechanism for disabling
-# algorithms during SSL/TLS security parameters negotiation, including
+# when using SSL/TLS/DTLS. This section describes the mechanism for disabling
+# algorithms during SSL/TLS/DTLS security parameters negotiation, including
# protocol version negotiation, cipher suites selection, peer authentication
# and key exchange mechanisms.
#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/solaris/native/libjava/ProcessHandleImpl_solaris.c Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "java_lang_ProcessHandleImpl.h"
+#include "java_lang_ProcessHandleImpl_Info.h"
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <procfs.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+
+/**
+ * Implementations of ProcessHandleImpl functions that are
+ * NOT common to all Unix variants:
+ * - getProcessPids0(pid, pidArray)
+ *
+ * Implementations of ProcessHandleImpl_Info
+ * - totalTime, startTime
+ * - Command
+ * - Arguments
+ */
+
+/*
+ * Signatures for internal OS specific functions.
+ */
+static pid_t parentPid(JNIEnv *env, pid_t pid);
+static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid);
+static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid);
+
+extern jstring uidToUser(JNIEnv* env, uid_t uid);
+
+/* Field id for jString 'command' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_commandID;
+
+/* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_argumentsID;
+
+/* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_totalTimeID;
+
+/* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_startTimeID;
+
+/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_userID;
+
+/* static value for clock ticks per second. */
+static long clock_ticks_per_second;
+
+/**************************************************************
+ * Static method to initialize field IDs and the ticks per second rate.
+ *
+ * Class: java_lang_ProcessHandleImpl_Info
+ * Method: initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs
+ (JNIEnv *env, jclass clazz) {
+
+ CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env,
+ clazz, "command", "Ljava/lang/String;"));
+ CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env,
+ clazz, "arguments", "[Ljava/lang/String;"));
+ CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env,
+ clazz, "totalTime", "J"));
+ CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env,
+ clazz, "startTime", "J"));
+ CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env,
+ clazz, "user", "Ljava/lang/String;"));
+ clock_ticks_per_second = sysconf(_SC_CLK_TCK);
+}
+
+/*
+ * Returns the parent pid of the requested pid.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: parent0
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0
+(JNIEnv *env, jobject obj, jlong jpid) {
+ pid_t pid = (pid_t) jpid;
+ pid_t ppid = -1;
+
+ if (pid == getpid()) {
+ ppid = getppid();
+ } else {
+ ppid = parentPid(env, pid);
+ }
+ return (jlong) ppid;
+}
+
+/*
+ * Returns the children of the requested pid and optionally each parent.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: getChildPids
+ * Signature: (J[J)I
+ *
+ * Reads /proc and accumulates any process who parent pid matches.
+ * The resulting pids are stored into the array of longs.
+ * The number of pids is returned if they all fit.
+ * If the array is too short, the desired length is returned.
+ */
+JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0
+(JNIEnv *env, jclass clazz, jlong jpid,
+ jlongArray jarray, jlongArray jparentArray)
+{
+ DIR* dir;
+ struct dirent* ptr;
+ pid_t pid = (pid_t) jpid;
+ size_t count = 0;
+ jlong* pids = NULL;
+ jlong* ppids = NULL;
+ size_t parentArraySize = 0;
+ size_t arraySize = 0;
+
+ arraySize = (*env)->GetArrayLength(env, jarray);
+ JNU_CHECK_EXCEPTION_RETURN(env, 0);
+ if (jparentArray != NULL) {
+ parentArraySize = (*env)->GetArrayLength(env, jparentArray);
+ JNU_CHECK_EXCEPTION_RETURN(env, 0);
+
+ if (arraySize != parentArraySize) {
+ JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
+ return 0;
+ }
+ }
+
+ /*
+ * To locate the children we scan /proc looking for files that have a
+ * positive integer as a filename.
+ */
+ if ((dir = opendir("/proc")) == NULL) {
+ JNU_ThrowByNameWithLastError(env,
+ "java/lang/Runtime", "Unable to open /proc");
+ return 0;
+ }
+
+ do { // Block to break out of on Exception
+ pids = (*env)->GetLongArrayElements(env, jarray, NULL);
+ if (pids == NULL) {
+ break;
+ }
+ if (jparentArray != NULL) {
+ ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL);
+ if (ppids == NULL) {
+ break;
+ }
+ }
+
+ while ((ptr = readdir(dir)) != NULL) {
+ pid_t ppid;
+
+ /* skip files that aren't numbers */
+ pid_t childpid = (pid_t) atoi(ptr->d_name);
+ if ((int) childpid <= 0) {
+ continue;
+ }
+
+ ppid = 0;
+ if (pid != 0 || jparentArray != NULL) {
+ // parentPid opens and reads /proc/pid/stat
+ ppid = parentPid(env, childpid);
+ }
+ if (pid == 0 || ppid == pid) {
+ if (count < arraySize) {
+ // Only store if it fits
+ pids[count] = (jlong) childpid;
+
+ if (ppids != NULL) {
+ // Store the parentPid
+ ppids[count] = (jlong) ppid;
+ }
+ }
+ count++; // Count to tabulate size needed
+ }
+ }
+ } while (0);
+
+ if (pids != NULL) {
+ (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
+ }
+ if (ppids != NULL) {
+ (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
+ }
+
+ closedir(dir);
+ // If more pids than array had size for; count will be greater than array size
+ return count;
+}
+
+/*
+ * Returns the parent pid of a given pid, or -1 if not found
+ */
+static pid_t parentPid(JNIEnv *env, pid_t pid) {
+ FILE* fp;
+ pstatus_t pstatus;
+ int statlen;
+ char fn[32];
+ int i, p;
+ char* s;
+
+ /*
+ * Try to open /proc/%d/status
+ */
+ snprintf(fn, sizeof fn, "/proc/%d/status", pid);
+ fp = fopen(fn, "r");
+ if (fp == NULL) {
+ return -1;
+ }
+
+ /*
+ * The format is: pid (command) state ppid ...
+ * As the command could be anything we must find the right most
+ * ")" and then skip the white spaces that follow it.
+ */
+ statlen = fread(&pstatus, 1, (sizeof pstatus), fp);
+ fclose(fp);
+ if (statlen < 0) {
+ return -1;
+ }
+ return (pid_t) pstatus.pr_ppid;
+}
+
+/**************************************************************
+ * Implementation of ProcessHandleImpl_Info native methods.
+ */
+
+/*
+ * Fill in the Info object from the OS information about the process.
+ *
+ * Class: java_lang_ProcessHandleImpl_Info
+ * Method: info0
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0
+ (JNIEnv *env, jobject jinfo, jlong jpid) {
+ pid_t pid = (pid_t) jpid;
+ getStatInfo(env, jinfo, pid);
+ getCmdlineInfo(env, jinfo, pid);
+}
+
+/**
+ * Read /proc/<pid>/stat and fill in the fields of the Info object.
+ * Gather the user and system times.
+ */
+static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
+ FILE* fp;
+ pstatus_t pstatus;
+ struct stat stat_buf;
+ int ret;
+ char fn[32];
+ int i, p;
+ char* s;
+ jlong totalTime;
+
+ /*
+ * Try to open /proc/%d/status
+ */
+ snprintf(fn, sizeof fn, "/proc/%d/status", pid);
+
+ if (stat(fn, &stat_buf) < 0) {
+ return;
+ }
+
+ fp = fopen(fn, "r");
+ if (fp == NULL) {
+ return;
+ }
+
+ ret = fread(&pstatus, 1, (sizeof pstatus), fp);
+ fclose(fp);
+ if (ret < 0) {
+ return;
+ }
+
+ totalTime = pstatus.pr_utime.tv_sec * 1000000000L + pstatus.pr_utime.tv_nsec +
+ pstatus.pr_stime.tv_sec * 1000000000L + pstatus.pr_stime.tv_nsec;
+
+ (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime);
+ JNU_CHECK_EXCEPTION(env);
+}
+
+static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
+ FILE* fp;
+ psinfo_t psinfo;
+ int ret;
+ char fn[32];
+ char exePath[PATH_MAX];
+ int i, p;
+ jlong startTime;
+ jobjectArray cmdArray;
+ jstring str = NULL;
+
+ /*
+ * try to open /proc/%d/psinfo
+ */
+ snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid);
+ fp = fopen(fn, "r");
+ if (fp == NULL) {
+ return;
+ }
+
+ /*
+ * The format is: pid (command) state ppid ...
+ * As the command could be anything we must find the right most
+ * ")" and then skip the white spaces that follow it.
+ */
+ ret = fread(&psinfo, 1, (sizeof psinfo), fp);
+ fclose(fp);
+ if (ret < 0) {
+ return;
+ }
+
+ CHECK_NULL((str = uidToUser(env, psinfo.pr_uid)));
+ (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, str);
+ JNU_CHECK_EXCEPTION(env);
+
+ startTime = (jlong)psinfo.pr_start.tv_sec * (jlong)1000 +
+ (jlong)psinfo.pr_start.tv_nsec / 1000000;
+ (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
+ JNU_CHECK_EXCEPTION(env);
+
+ /*
+ * The path to the executable command is the link in /proc/<pid>/paths/a.out.
+ */
+ snprintf(fn, sizeof fn, "/proc/%d/path/a.out", pid);
+ if ((ret = readlink(fn, exePath, PATH_MAX - 1)) < 0) {
+ return;
+ }
+
+ // null terminate and create String to store for command
+ exePath[ret] = '\0';
+ CHECK_NULL(str = JNU_NewStringPlatform(env, exePath));
+ (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, str);
+ JNU_CHECK_EXCEPTION(env);
+}
+
--- a/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java Thu Jun 04 18:49:37 2015 -0700
@@ -39,9 +39,7 @@
import java.util.EnumSet;
import java.util.Locale;
import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.security.AccessController;
import static java.security.AccessController.doPrivileged;
@@ -50,8 +48,7 @@
import java.security.PrivilegedExceptionAction;
/**
- * This java.lang.Process subclass in the UNIX environment is for the exclusive use of
- * ProcessBuilder.start() to create new processes.
+ * java.lang.Process subclass in the UNIX environment.
*
* @author Mario Wolczko and Ross Knippel.
* @author Konstantin Kladko (ported to Linux and Bsd)
@@ -63,12 +60,16 @@
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
+ // Linux platforms support a normal (non-forcible) kill signal.
+ static final boolean SUPPORTS_NORMAL_TERMINATION = true;
+
private final int pid;
+ private final ProcessHandle processHandle;
private int exitcode;
private boolean hasExited;
private /* final */ OutputStream stdin;
- private /* final */ InputStream stdout;
+ private /* final */ InputStream stdout;
private /* final */ InputStream stderr;
// only used on Solaris
@@ -97,7 +98,7 @@
Platform(LaunchMechanism ... launchMechanisms) {
this.defaultLaunchMechanism = launchMechanisms[0];
this.validLaunchMechanisms =
- EnumSet.copyOf(Arrays.asList(launchMechanisms));
+ EnumSet.copyOf(Arrays.asList(launchMechanisms));
}
@SuppressWarnings("fallthrough")
@@ -121,43 +122,43 @@
String helperPath() {
return AccessController.doPrivileged(
- (PrivilegedAction<String>) () ->
- helperPath(System.getProperty("java.home"),
- System.getProperty("os.arch"))
+ (PrivilegedAction<String>) () ->
+ helperPath(System.getProperty("java.home"),
+ System.getProperty("os.arch"))
);
}
LaunchMechanism launchMechanism() {
return AccessController.doPrivileged(
- (PrivilegedAction<LaunchMechanism>) () -> {
- String s = System.getProperty(
- "jdk.lang.Process.launchMechanism");
- LaunchMechanism lm;
- if (s == null) {
- lm = defaultLaunchMechanism;
- s = lm.name().toLowerCase(Locale.ENGLISH);
- } else {
- try {
- lm = LaunchMechanism.valueOf(
- s.toUpperCase(Locale.ENGLISH));
- } catch (IllegalArgumentException e) {
- lm = null;
- }
+ (PrivilegedAction<LaunchMechanism>) () -> {
+ String s = System.getProperty(
+ "jdk.lang.Process.launchMechanism");
+ LaunchMechanism lm;
+ if (s == null) {
+ lm = defaultLaunchMechanism;
+ s = lm.name().toLowerCase(Locale.ENGLISH);
+ } else {
+ try {
+ lm = LaunchMechanism.valueOf(
+ s.toUpperCase(Locale.ENGLISH));
+ } catch (IllegalArgumentException e) {
+ lm = null;
}
- if (lm == null || !validLaunchMechanisms.contains(lm)) {
- throw new Error(
- s + " is not a supported " +
- "process launch mechanism on this platform."
- );
- }
- return lm;
}
+ if (lm == null || !validLaunchMechanisms.contains(lm)) {
+ throw new Error(
+ s + " is not a supported " +
+ "process launch mechanism on this platform."
+ );
+ }
+ return lm;
+ }
);
}
static Platform get() {
String osName = AccessController.doPrivileged(
- (PrivilegedAction<String>) () -> System.getProperty("os.name")
+ (PrivilegedAction<String>) () -> System.getProperty("os.name")
);
if (osName.equals("Linux")) { return LINUX; }
@@ -173,17 +174,14 @@
private static final LaunchMechanism launchMechanism = platform.launchMechanism();
private static final byte[] helperpath = toCString(platform.helperPath());
- /* this is for the reaping thread */
- private native int waitForProcessExit(int pid);
-
private static byte[] toCString(String s) {
if (s == null)
return null;
byte[] bytes = s.getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0,
- result, 0,
- bytes.length);
+ result, 0,
+ bytes.length);
result[result.length-1] = (byte)0;
return result;
}
@@ -304,30 +302,7 @@
byte[] dir,
int[] fds,
boolean redirectErrorStream)
- throws IOException;
-
- /**
- * The thread pool of "process reaper" daemon threads.
- */
- private static final Executor processReaperExecutor =
- doPrivileged((PrivilegedAction<Executor>) () -> {
-
- ThreadGroup tg = Thread.currentThread().getThreadGroup();
- while (tg.getParent() != null) tg = tg.getParent();
- ThreadGroup systemThreadGroup = tg;
-
- ThreadFactory threadFactory = grimReaper -> {
- // Our thread stack requirement is quite modest.
- Thread t = new Thread(systemThreadGroup, grimReaper,
- "process reaper", 32768);
- t.setDaemon(true);
- // A small attempt (probably futile) to avoid priority inversion
- t.setPriority(Thread.MAX_PRIORITY);
- return t;
- };
-
- return Executors.newCachedThreadPool(threadFactory);
- });
+ throws IOException;
private ProcessImpl(final byte[] prog,
final byte[] argBlock, final int argc,
@@ -338,13 +313,14 @@
throws IOException {
pid = forkAndExec(launchMechanism.ordinal() + 1,
- helperpath,
- prog,
- argBlock, argc,
- envBlock, envc,
- dir,
- fds,
- redirectErrorStream);
+ helperpath,
+ prog,
+ argBlock, argc,
+ envBlock, envc,
+ dir,
+ fds,
+ redirectErrorStream);
+ processHandle = ProcessHandleImpl.getUnchecked(pid);
try {
doPrivileged((PrivilegedExceptionAction<Void>) () -> {
@@ -371,18 +347,16 @@
new ProcessPipeOutputStream(fds[0]);
stdout = (fds[1] == -1) ?
- ProcessBuilder.NullInputStream.INSTANCE :
- new ProcessPipeInputStream(fds[1]);
+ ProcessBuilder.NullInputStream.INSTANCE :
+ new ProcessPipeInputStream(fds[1]);
stderr = (fds[2] == -1) ?
- ProcessBuilder.NullInputStream.INSTANCE :
- new ProcessPipeInputStream(fds[2]);
+ ProcessBuilder.NullInputStream.INSTANCE :
+ new ProcessPipeInputStream(fds[2]);
- processReaperExecutor.execute(() -> {
- int exitcode = waitForProcessExit(pid);
-
+ ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
synchronized (this) {
- this.exitcode = exitcode;
+ this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
this.hasExited = true;
this.notifyAll();
}
@@ -395,6 +369,8 @@
if (stdin instanceof ProcessPipeOutputStream)
((ProcessPipeOutputStream) stdin).processExited();
+
+ return null;
});
break;
@@ -402,18 +378,18 @@
stdin = (fds[0] == -1) ?
ProcessBuilder.NullOutputStream.INSTANCE :
new BufferedOutputStream(
- new FileOutputStream(newFileDescriptor(fds[0])));
+ new FileOutputStream(newFileDescriptor(fds[0])));
stdout = (fds[1] == -1) ?
- ProcessBuilder.NullInputStream.INSTANCE :
- new BufferedInputStream(
- stdout_inner_stream =
- new DeferredCloseInputStream(
- newFileDescriptor(fds[1])));
+ ProcessBuilder.NullInputStream.INSTANCE :
+ new BufferedInputStream(
+ stdout_inner_stream =
+ new DeferredCloseInputStream(
+ newFileDescriptor(fds[1])));
stderr = (fds[2] == -1) ?
- ProcessBuilder.NullInputStream.INSTANCE :
- new DeferredCloseInputStream(newFileDescriptor(fds[2]));
+ ProcessBuilder.NullInputStream.INSTANCE :
+ new DeferredCloseInputStream(newFileDescriptor(fds[2]));
/*
* For each subprocess forked a corresponding reaper task
@@ -423,14 +399,13 @@
* exitStatus() to be safely executed in parallel (and they
* need no native code).
*/
- processReaperExecutor.execute(() -> {
- int exitcode = waitForProcessExit(pid);
-
+ ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
synchronized (this) {
- this.exitcode = exitcode;
+ this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
this.hasExited = true;
this.notifyAll();
}
+ return null;
});
break;
@@ -440,18 +415,16 @@
new ProcessPipeOutputStream(fds[0]);
stdout = (fds[1] == -1) ?
- ProcessBuilder.NullInputStream.INSTANCE :
- new DeferredCloseProcessPipeInputStream(fds[1]);
+ ProcessBuilder.NullInputStream.INSTANCE :
+ new DeferredCloseProcessPipeInputStream(fds[1]);
stderr = (fds[2] == -1) ?
- ProcessBuilder.NullInputStream.INSTANCE :
- new DeferredCloseProcessPipeInputStream(fds[2]);
+ ProcessBuilder.NullInputStream.INSTANCE :
+ new DeferredCloseProcessPipeInputStream(fds[2]);
- processReaperExecutor.execute(() -> {
- int exitcode = waitForProcessExit(pid);
-
+ ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
synchronized (this) {
- this.exitcode = exitcode;
+ this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
this.hasExited = true;
this.notifyAll();
}
@@ -464,6 +437,8 @@
if (stdin instanceof ProcessPipeOutputStream)
((ProcessPipeOutputStream) stdin).processExited();
+
+ return null;
});
break;
@@ -492,7 +467,7 @@
@Override
public synchronized boolean waitFor(long timeout, TimeUnit unit)
- throws InterruptedException
+ throws InterruptedException
{
long remainingNanos = unit.toNanos(timeout); // throw NPE before other conditions
if (hasExited) return true;
@@ -517,8 +492,6 @@
return exitcode;
}
- private static native void destroyProcess(int pid, boolean force);
-
private void destroy(boolean force) {
switch (platform) {
case LINUX:
@@ -532,7 +505,7 @@
// soon, so this is quite safe.
synchronized (this) {
if (!hasExited)
- destroyProcess(pid, force);
+ ProcessHandleImpl.destroyProcess(pid, force);
}
try { stdin.close(); } catch (IOException ignored) {}
try { stdout.close(); } catch (IOException ignored) {}
@@ -548,14 +521,14 @@
// soon, so this is quite safe.
synchronized (this) {
if (!hasExited)
- destroyProcess(pid, force);
+ ProcessHandleImpl.destroyProcess(pid, force);
try {
stdin.close();
if (stdout_inner_stream != null)
stdout_inner_stream.closeDeferred(stdout);
if (stderr instanceof DeferredCloseInputStream)
((DeferredCloseInputStream) stderr)
- .closeDeferred(stderr);
+ .closeDeferred(stderr);
} catch (IOException e) {
// ignore
}
@@ -566,6 +539,27 @@
}
}
+ @Override
+ public CompletableFuture<Process> onExit() {
+ return ProcessHandleImpl.completion(pid, false)
+ .handleAsync((exitStatus, unusedThrowable) -> this);
+ }
+
+ @Override
+ public ProcessHandle toHandle() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("manageProcess"));
+ }
+ return processHandle;
+ }
+
+ @Override
+ public boolean supportsNormalTermination() {
+ return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
+ }
+
+ @Override
public void destroy() {
destroy(false);
}
@@ -629,8 +623,8 @@
byte[] stragglers = drainInputStream(in);
in.close();
this.in = (stragglers == null) ?
- ProcessBuilder.NullInputStream.INSTANCE :
- new ByteArrayInputStream(stragglers);
+ ProcessBuilder.NullInputStream.INSTANCE :
+ new ByteArrayInputStream(stragglers);
}
} catch (IOException ignored) {}
}
@@ -797,7 +791,7 @@
*
*/
private static class DeferredCloseProcessPipeInputStream
- extends BufferedInputStream {
+ extends BufferedInputStream {
private final Object closeLock = new Object();
private int useCount = 0;
--- a/jdk/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java Thu Jun 04 18:49:37 2015 -0700
@@ -80,6 +80,15 @@
return options;
}
+ protected void socketSetOption(int opt, Object val) throws SocketException {
+ try {
+ socketSetOption0(opt, val);
+ } catch (SocketException se) {
+ if (!connected)
+ throw se;
+ }
+ }
+
protected synchronized native void bind0(int lport, InetAddress laddr)
throws SocketException;
@@ -112,7 +121,7 @@
protected native void datagramSocketClose();
- protected native void socketSetOption(int opt, Object val)
+ protected native void socketSetOption0(int opt, Object val)
throws SocketException;
protected native Object socketGetOption(int opt) throws SocketException;
--- a/jdk/src/java.base/unix/classes/java/net/PlainSocketImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/unix/classes/java/net/PlainSocketImpl.java Thu Jun 04 18:49:37 2015 -0700
@@ -94,6 +94,15 @@
return options;
}
+ protected void socketSetOption(int opt, boolean b, Object val) throws SocketException {
+ try {
+ socketSetOption0(opt, b, val);
+ } catch (SocketException se) {
+ if (socket == null || !socket.isConnected())
+ throw se;
+ }
+ }
+
native void socketCreate(boolean isServer) throws IOException;
native void socketConnect(InetAddress address, int port, int timeout)
@@ -114,7 +123,7 @@
static native void initProto();
- native void socketSetOption(int cmd, boolean on, Object value)
+ native void socketSetOption0(int cmd, boolean on, Object value)
throws SocketException;
native int socketGetOption(int opt, Object iaContainerObj) throws SocketException;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,769 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "java_lang_ProcessHandleImpl.h"
+#include "java_lang_ProcessHandleImpl_Info.h"
+
+
+#include <stdio.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+
+/**
+ * Implementations of ProcessHandleImpl functions that are common to all
+ * Unix variants:
+ * - waitForProcessExit0(pid, reap)
+ * - getCurrentPid0()
+ * - destroy0(pid, force)
+ */
+
+
+#ifndef WIFEXITED
+#define WIFEXITED(status) (((status)&0xFF) == 0)
+#endif
+
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(status) (((status)>>8)&0xFF)
+#endif
+
+#ifndef WIFSIGNALED
+#define WIFSIGNALED(status) (((status)&0xFF) > 0 && ((status)&0xFF00) == 0)
+#endif
+
+#ifndef WTERMSIG
+#define WTERMSIG(status) ((status)&0x7F)
+#endif
+
+#define RESTARTABLE(_cmd, _result) do { \
+ do { \
+ _result = _cmd; \
+ } while((_result == -1) && (errno == EINTR)); \
+} while(0)
+
+#define RESTARTABLE_RETURN_PTR(_cmd, _result) do { \
+ do { \
+ _result = _cmd; \
+ } while((_result == NULL) && (errno == EINTR)); \
+} while(0)
+
+
+/* Block until a child process exits and return its exit code.
+ * Note, can only be called once for any given pid if reapStatus = true.
+ */
+JNIEXPORT jint JNICALL
+Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env,
+ jclass junk,
+ jlong jpid,
+ jboolean reapStatus)
+{
+ pid_t pid = (pid_t)jpid;
+ errno = 0;
+
+ if (reapStatus != JNI_FALSE) {
+ /* Wait for the child process to exit.
+ * waitpid() is standard, so use it on all POSIX platforms.
+ * It is known to work when blocking to wait for the pid
+ * This returns immediately if the child has already exited.
+ */
+ int status;
+ while (waitpid(pid, &status, 0) < 0) {
+ switch (errno) {
+ case ECHILD: return 0;
+ case EINTR: break;
+ default: return -1;
+ }
+ }
+
+ if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ /* The child exited because of a signal.
+ * The best value to return is 0x80 + signal number,
+ * because that is what all Unix shells do, and because
+ * it allows callers to distinguish between process exit and
+ * process death by signal.
+ * Unfortunately, the historical behavior on Solaris is to return
+ * the signal number, and we preserve this for compatibility. */
+#ifdef __solaris__
+ return WTERMSIG(status);
+#else
+ return 0x80 + WTERMSIG(status);
+#endif
+ } else {
+ return status;
+ }
+ } else {
+ /*
+ * Wait for the child process to exit without reaping the exitValue.
+ * waitid() is standard on all POSIX platforms.
+ * Note: waitid on Mac OS X 10.7 seems to be broken;
+ * it does not return the exit status consistently.
+ */
+ siginfo_t siginfo;
+ int options = WEXITED | WNOWAIT;
+ memset(&siginfo, 0, sizeof siginfo);
+ while (waitid(P_PID, pid, &siginfo, options) < 0) {
+ switch (errno) {
+ case ECHILD: return 0;
+ case EINTR: break;
+ default: return -1;
+ }
+ }
+
+ if (siginfo.si_code == CLD_EXITED) {
+ /*
+ * The child exited normally; get its exit code.
+ */
+ return siginfo.si_status;
+ } else if (siginfo.si_code == CLD_KILLED || siginfo.si_code == CLD_DUMPED) {
+ /* The child exited because of a signal.
+ * The best value to return is 0x80 + signal number,
+ * because that is what all Unix shells do, and because
+ * it allows callers to distinguish between process exit and
+ * process death by signal.
+ * Unfortunately, the historical behavior on Solaris is to return
+ * the signal number, and we preserve this for compatibility. */
+ #ifdef __solaris__
+ return WTERMSIG(siginfo.si_status);
+ #else
+ return 0x80 + WTERMSIG(siginfo.si_status);
+ #endif
+ } else {
+ /*
+ * Unknown exit code; pass it through.
+ */
+ return siginfo.si_status;
+ }
+ }
+}
+
+/*
+ * Class: java_lang_ProcessHandleImpl
+ * Method: getCurrentPid0
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_getCurrentPid0
+(JNIEnv *env, jclass clazz) {
+ pid_t pid = getpid();
+ return (jlong) pid;
+}
+
+/*
+ * Class: java_lang_ProcessHandleImpl
+ * Method: isAlive0
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_isAlive0
+(JNIEnv *env, jobject obj, jlong jpid) {
+ pid_t pid = (pid_t) jpid;
+ return (kill(pid, 0) < 0) ? JNI_FALSE : JNI_TRUE;
+}
+
+/*
+ * Class: java_lang_ProcessHandleImpl
+ * Method: destroy0
+ * Signature: (Z)Z
+ */
+JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_destroy0
+(JNIEnv *env, jobject obj, jlong jpid, jboolean force) {
+ pid_t pid = (pid_t) jpid;
+ int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM;
+ return (kill(pid, sig) >= 0);
+
+}
+
+/**
+ * Size of password or group entry when not available via sysconf
+ */
+#define ENT_BUF_SIZE 1024
+
+/**
+ * Return a strong username for the uid_t or null.
+ */
+jstring uidToUser(JNIEnv* env, uid_t uid) {
+ int result = 0;
+ int buflen;
+ char* pwbuf;
+ jstring name = NULL;
+
+ /* allocate buffer for password record */
+ buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (buflen == -1)
+ buflen = ENT_BUF_SIZE;
+ pwbuf = (char*)malloc(buflen);
+ if (pwbuf == NULL) {
+ JNU_ThrowOutOfMemoryError(env, "Unable to open getpwent");
+ } else {
+ struct passwd pwent;
+ struct passwd* p = NULL;
+
+#ifdef __solaris__
+ RESTARTABLE_RETURN_PTR(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen), p);
+#else
+ RESTARTABLE(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen, &p), result);
+#endif
+
+ // Return the Java String if a name was found
+ if (result == 0 && p != NULL &&
+ p->pw_name != NULL && *(p->pw_name) != '\0') {
+ name = JNU_NewStringPlatform(env, p->pw_name);
+ }
+ free(pwbuf);
+ }
+ return name;
+}
+
+/**
+ * Implementations of ProcessHandleImpl functions that are common to
+ * (some) Unix variants:
+ * - getProcessPids0(pid, pidArray, parentArray)
+ */
+
+#if defined(__linux__) || defined(__AIX__)
+
+/*
+ * Signatures for internal OS specific functions.
+ */
+static pid_t parentPid(JNIEnv *env, pid_t pid);
+static jint getChildren(JNIEnv *env, jlong jpid,
+ jlongArray array, jlongArray jparentArray);
+
+static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid);
+static void getCmdlineInfo(JNIEnv *env, pid_t pid, jobject jinfo);
+static long long getBoottime(JNIEnv *env);
+
+jstring uidToUser(JNIEnv* env, uid_t uid);
+
+/* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_commandID;
+
+/* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_argumentsID;
+
+/* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_totalTimeID;
+
+/* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_startTimeID;
+
+/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_userID;
+
+/* static value for clock ticks per second. */
+static long clock_ticks_per_second;
+
+/* A static offset in milliseconds since boot. */
+static long long bootTime_ms;
+
+/**************************************************************
+ * Static method to initialize field IDs and the ticks per second rate.
+ *
+ * Class: java_lang_ProcessHandleImpl_Info
+ * Method: initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs
+ (JNIEnv *env, jclass clazz) {
+
+ CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env,
+ clazz, "command", "Ljava/lang/String;"));
+ CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env,
+ clazz, "arguments", "[Ljava/lang/String;"));
+ CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env,
+ clazz, "totalTime", "J"));
+ CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env,
+ clazz, "startTime", "J"));
+ CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env,
+ clazz, "user", "Ljava/lang/String;"));
+ clock_ticks_per_second = sysconf(_SC_CLK_TCK);
+ bootTime_ms = getBoottime(env);
+}
+
+/*
+ * Returns the parent pid of the requested pid.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: parent0
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0
+(JNIEnv *env, jobject obj, jlong jpid) {
+ pid_t pid = (pid_t) jpid;
+ pid_t ppid = -1;
+
+ pid_t mypid = getpid();
+ if (pid == mypid) {
+ ppid = getppid();
+ } else {
+ ppid = parentPid(env, pid);
+ }
+ return (jlong) ppid;
+}
+
+/*
+ * Returns the children of the requested pid and optionally each parent.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: getChildPids
+ * Signature: (J[J[J)I
+ */
+JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0
+(JNIEnv *env, jclass clazz, jlong jpid,
+ jlongArray jarray, jlongArray jparentArray) {
+ return getChildren(env, jpid, jarray, jparentArray);
+}
+
+/*
+ * Reads /proc and accumulates any process who parent pid matches.
+ * The resulting pids are stored into the array of longs.
+ * The number of pids is returned if they all fit.
+ * If the array is too short, the negative of the desired length is returned.
+ */
+static jint getChildren(JNIEnv *env, jlong jpid,
+ jlongArray jarray, jlongArray jparentArray) {
+ DIR* dir;
+ struct dirent* ptr;
+ pid_t pid = (pid_t) jpid;
+ pid_t ppid = 0;
+ size_t count = 0;
+ jlong* pids = NULL;
+ jlong* ppids = NULL;
+ size_t parentArraySize = 0;
+ size_t arraySize = 0;
+
+ arraySize = (*env)->GetArrayLength(env, jarray);
+ JNU_CHECK_EXCEPTION_RETURN(env, -1);
+ if (jparentArray != NULL) {
+ parentArraySize = (*env)->GetArrayLength(env, jparentArray);
+ JNU_CHECK_EXCEPTION_RETURN(env, -1);
+
+ if (arraySize != parentArraySize) {
+ JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
+ return 0;
+ }
+ }
+
+ /*
+ * To locate the children we scan /proc looking for files that have a
+ * position integer as a filename.
+ */
+ if ((dir = opendir("/proc")) == NULL) {
+ JNU_ThrowByNameWithLastError(env,
+ "java/lang/Runtime", "Unable to open /proc");
+ return -1;
+ }
+
+ do { // Block to break out of on Exception
+ pids = (*env)->GetLongArrayElements(env, jarray, NULL);
+ if (pids == NULL) {
+ break;
+ }
+ if (jparentArray != NULL) {
+ ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL);
+ if (ppids == NULL) {
+ break;
+ }
+ }
+
+ while ((ptr = readdir(dir)) != NULL) {
+ /* skip files that aren't numbers */
+ pid_t childpid = (pid_t) atoi(ptr->d_name);
+ if ((int) childpid <= 0) {
+ continue;
+ }
+
+ ppid = 0;
+ if (pid != 0 || jparentArray != NULL) {
+ // parentPid opens and reads /proc/pid/stat
+ ppid = parentPid(env, childpid);
+ }
+ if (pid == 0 || ppid == pid) {
+ if (count < arraySize) {
+ // Only store if it fits
+ pids[count] = (jlong) childpid;
+
+ if (ppids != NULL) {
+ // Store the parentPid
+ ppids[count] = (jlong) ppid;
+ }
+ }
+ count++; // Count to tabulate size needed
+ }
+ }
+ } while (0);
+
+ if (pids != NULL) {
+ (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
+ }
+ if (ppids != NULL) {
+ (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
+ }
+
+ closedir(dir);
+ // If more pids than array had size for; count will be greater than array size
+ return count;
+}
+
+/*
+ * Returns the parent pid of a given pid, or -1 if not found
+ */
+static pid_t parentPid(JNIEnv *env, pid_t pid) {
+ char state;
+ FILE* fp;
+ char stat[2048];
+ int statlen;
+ char fn[32];
+ int i, p;
+ char* s;
+
+ /*
+ * try to open /proc/%d/stat
+ */
+ snprintf(fn, sizeof fn, "/proc/%d/stat", pid);
+ fp = fopen(fn, "r");
+ if (fp == NULL) {
+ return -1;
+ }
+
+ /*
+ * The format is: pid (command) state ppid ...
+ * As the command could be anything we must find the right most
+ * ")" and then skip the white spaces that follow it.
+ */
+ statlen = fread(stat, 1, (sizeof stat - 1), fp);
+ fclose(fp);
+ if (statlen < 0) {
+ return -1;
+ }
+
+ stat[statlen] = '\0';
+ s = strrchr(stat, ')');
+ if (s == NULL) {
+ return -1;
+ }
+ do s++; while (isspace(*s));
+ i = sscanf(s, "%c %d", &state, &p);
+ if (i != 2) {
+ return (pid_t)-1;
+ }
+ return (pid_t) p;
+}
+
+/**************************************************************
+ * Implementation of ProcessHandleImpl_Info native methods.
+ */
+
+/*
+ * Fill in the Info object from the OS information about the process.
+ *
+ * Class: java_lang_ProcessHandleImpl_Info
+ * Method: info0
+ * Signature: (JLjava/lang/ProcessHandle/Info;)I
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0
+ (JNIEnv *env, jobject jinfo, jlong jpid) {
+ pid_t pid = (pid_t) jpid;
+ getStatInfo(env, jinfo, (pid_t)pid);
+ getCmdlineInfo(env, pid, jinfo);
+}
+
+/**
+ * Read /proc/<pid>/stat and fill in the fields of the Info object.
+ * The executable name, plus the user, system, and start times are gathered.
+ */
+static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
+ char state;
+ FILE* fp;
+ char buffer[2048];
+ struct stat stat_buf;
+ int statlen;
+ char fn[32];
+ int i, ppid = -2;
+ char* s;
+ char *cmd;
+ jstring name = NULL;
+ unsigned long userTime = 0; // clock tics
+ unsigned long totalTime = 0; // clock tics
+ jlong total = 0; // nano seconds
+ unsigned long long startTime = 0; // microseconds
+
+ /*
+ * Try to stat and then open /proc/%d/stat
+ */
+ snprintf(fn, sizeof fn, "/proc/%d/stat", pid);
+
+ if (stat(fn, &stat_buf) < 0) {
+ return;
+ }
+
+ CHECK_NULL((name = uidToUser(env, stat_buf.st_uid)));
+ (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name);
+ JNU_CHECK_EXCEPTION(env);
+
+ fp = fopen(fn, "r");
+ if (fp == NULL) {
+ return;
+ }
+
+ /*
+ * The format is: pid (command) state ppid ...
+ * As the command could be anything we must find the right most
+ * ")" and then skip the white spaces that follow it.
+ */
+ statlen = fread(buffer, 1, (sizeof buffer - 1), fp);
+ fclose(fp);
+ if (statlen < 0) {
+ return;
+ }
+
+ buffer[statlen] = '\0';
+ s = strchr(buffer, '(');
+ if (s == NULL) {
+ return;
+ }
+ // Found start of command, skip to end
+ s++;
+ s = strrchr(s, ')');
+ if (s == NULL) {
+ return;
+ }
+ s++;
+
+ // Scan the needed fields from status, retaining only ppid(4),
+ // utime (14), stime(15), starttime(22)
+ i = sscanf(s, " %c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu",
+ &state, &ppid, &userTime, &totalTime, &startTime);
+ if (i != 5) {
+ return; // not all values parsed; return error
+ }
+
+ total = (userTime + totalTime) * (jlong)(1000000000 / clock_ticks_per_second);
+
+ startTime = bootTime_ms + ((startTime * 1000) / clock_ticks_per_second);
+
+ (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, total);
+ JNU_CHECK_EXCEPTION(env);
+ (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
+ JNU_CHECK_EXCEPTION(env);
+}
+
+/**
+ * Construct the argument array by parsing the arguments from the sequence
+ * of arguments. The zero'th arg is the command executable
+ */
+static int fillArgArray(JNIEnv *env, jobject jinfo,
+ int nargs, char *cp, char *argsEnd, jstring cmdexe) {
+ jobject argsArray;
+ int i;
+
+ if (nargs < 1) {
+ return 0;
+ }
+
+ if (cmdexe == NULL) {
+ // Create a string from arg[0]
+ CHECK_NULL_RETURN((cmdexe = JNU_NewStringPlatform(env, cp)), -1);
+ }
+ (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, cmdexe);
+ JNU_CHECK_EXCEPTION_RETURN(env, -3);
+
+ // Create a String array for nargs-1 elements
+ argsArray = (*env)->NewObjectArray(env, nargs - 1, JNU_ClassString(env), NULL);
+ CHECK_NULL_RETURN(argsArray, -1);
+
+ for (i = 0; i < nargs - 1; i++) {
+ jstring str = NULL;
+
+ cp += strnlen(cp, (argsEnd - cp)) + 1;
+ if (cp > argsEnd || *cp == '\0') {
+ return -2; // Off the end pointer or an empty argument is an error
+ }
+
+ CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1);
+
+ (*env)->SetObjectArrayElement(env, argsArray, i, str);
+ JNU_CHECK_EXCEPTION_RETURN(env, -3);
+ }
+ (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray);
+ JNU_CHECK_EXCEPTION_RETURN(env, -4);
+ return 0;
+}
+
+
+static void getCmdlineInfo(JNIEnv *env, pid_t pid, jobject jinfo) {
+ int fd;
+ int cmdlen = 0;
+ char *cmdline = NULL, *cmdEnd; // used for command line args and exe
+ jstring cmdexe = NULL;
+ char fn[32];
+
+ /*
+ * Try to open /proc/%d/cmdline
+ */
+ snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid);
+ if ((fd = open(fn, O_RDONLY)) < 0) {
+ return;
+ }
+
+ do { // Block to break out of on errors
+ int i;
+ char *s;
+
+ cmdline = (char*)malloc(PATH_MAX);
+ if (cmdline == NULL) {
+ break;
+ }
+
+ /*
+ * The path to the executable command is the link in /proc/<pid>/exe.
+ */
+ snprintf(fn, sizeof fn, "/proc/%d/exe", pid);
+ if ((cmdlen = readlink(fn, cmdline, PATH_MAX - 1)) > 0) {
+ // null terminate and create String to store for command
+ cmdline[cmdlen] = '\0';
+ cmdexe = JNU_NewStringPlatform(env, cmdline);
+ (*env)->ExceptionClear(env); // unconditionally clear any exception
+ }
+
+ /*
+ * The buffer format is the arguments nul terminated with an extra nul.
+ */
+ cmdlen = read(fd, cmdline, PATH_MAX-1);
+ if (cmdlen < 0) {
+ break;
+ }
+
+ // Terminate the buffer and count the arguments
+ cmdline[cmdlen] = '\0';
+ cmdEnd = &cmdline[cmdlen + 1];
+ for (s = cmdline,i = 0; *s != '\0' && (s < cmdEnd); i++) {
+ s += strnlen(s, (cmdEnd - s)) + 1;
+ }
+
+ if (fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe) < 0) {
+ break;
+ }
+ } while (0);
+
+ if (cmdline != NULL) {
+ free(cmdline);
+ }
+ if (fd >= 0) {
+ close(fd);
+ }
+}
+
+/**
+ * Read the boottime from /proc/stat.
+ */
+static long long getBoottime(JNIEnv *env) {
+ FILE *fp;
+ char *line = NULL;
+ size_t len = 0;
+ long long bootTime = 0;
+
+ fp = fopen("/proc/stat", "r");
+ if (fp == NULL) {
+ return -1;
+ }
+
+ while (getline(&line, &len, fp) != -1) {
+ if (sscanf(line, "btime %llu", &bootTime) == 1) {
+ break;
+ }
+ }
+ free(line);
+
+ if (fp != 0) {
+ fclose(fp);
+ }
+
+ return bootTime * 1000;
+}
+
+#endif // defined(__linux__) || defined(__AIX__)
+
+
+/* Block until a child process exits and return its exit code.
+ Note, can only be called once for any given pid. */
+JNIEXPORT jint JNICALL
+Java_java_lang_ProcessImpl_waitForProcessExit(JNIEnv* env,
+ jobject junk,
+ jint pid)
+{
+ /* We used to use waitid() on Solaris, waitpid() on Linux, but
+ * waitpid() is more standard, so use it on all POSIX platforms. */
+ int status;
+ /* Wait for the child process to exit. This returns immediately if
+ the child has already exited. */
+ while (waitpid(pid, &status, 0) < 0) {
+ switch (errno) {
+ case ECHILD: return 0;
+ case EINTR: break;
+ default: return -1;
+ }
+ }
+
+ if (WIFEXITED(status)) {
+ /*
+ * The child exited normally; get its exit code.
+ */
+ return WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ /* The child exited because of a signal.
+ * The best value to return is 0x80 + signal number,
+ * because that is what all Unix shells do, and because
+ * it allows callers to distinguish between process exit and
+ * process death by signal.
+ * Unfortunately, the historical behavior on Solaris is to return
+ * the signal number, and we preserve this for compatibility. */
+#ifdef __solaris__
+ return WTERMSIG(status);
+#else
+ return 0x80 + WTERMSIG(status);
+#endif
+ } else {
+ /*
+ * Unknown exit code; pass it through.
+ */
+ return status;
+ }
+}
+
+
--- a/jdk/src/java.base/unix/native/libjava/ProcessImpl_md.c Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/unix/native/libjava/ProcessImpl_md.c Thu Jun 04 18:49:37 2015 -0700
@@ -226,52 +226,6 @@
#define WTERMSIG(status) ((status)&0x7F)
#endif
-/* Block until a child process exits and return its exit code.
- Note, can only be called once for any given pid. */
-JNIEXPORT jint JNICALL
-Java_java_lang_ProcessImpl_waitForProcessExit(JNIEnv* env,
- jobject junk,
- jint pid)
-{
- /* We used to use waitid() on Solaris, waitpid() on Linux, but
- * waitpid() is more standard, so use it on all POSIX platforms. */
- int status;
- /* Wait for the child process to exit. This returns immediately if
- the child has already exited. */
- while (waitpid(pid, &status, 0) < 0) {
- switch (errno) {
- case ECHILD: return 0;
- case EINTR: break;
- default: return -1;
- }
- }
-
- if (WIFEXITED(status)) {
- /*
- * The child exited normally; get its exit code.
- */
- return WEXITSTATUS(status);
- } else if (WIFSIGNALED(status)) {
- /* The child exited because of a signal.
- * The best value to return is 0x80 + signal number,
- * because that is what all Unix shells do, and because
- * it allows callers to distinguish between process exit and
- * process death by signal.
- * Unfortunately, the historical behavior on Solaris is to return
- * the signal number, and we preserve this for compatibility. */
-#ifdef __solaris__
- return WTERMSIG(status);
-#else
- return 0x80 + WTERMSIG(status);
-#endif
- } else {
- /*
- * Unknown exit code; pass it through.
- */
- return status;
- }
-}
-
static const char *
getBytes(JNIEnv *env, jbyteArray arr)
{
@@ -686,12 +640,3 @@
goto Finally;
}
-JNIEXPORT void JNICALL
-Java_java_lang_ProcessImpl_destroyProcess(JNIEnv *env,
- jobject junk,
- jint pid,
- jboolean force)
-{
- int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM;
- kill(pid, sig);
-}
--- a/jdk/src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c Thu Jun 04 18:49:37 2015 -0700
@@ -1294,11 +1294,11 @@
/*
* Class: java_net_PlainDatagramSocketImpl
- * Method: socketSetOption
+ * Method: socketSetOption0
* Signature: (ILjava/lang/Object;)V
*/
JNIEXPORT void JNICALL
-Java_java_net_PlainDatagramSocketImpl_socketSetOption(JNIEnv *env,
+Java_java_net_PlainDatagramSocketImpl_socketSetOption0(JNIEnv *env,
jobject this,
jint opt,
jobject value) {
--- a/jdk/src/java.base/unix/native/libnet/PlainSocketImpl.c Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/unix/native/libnet/PlainSocketImpl.c Thu Jun 04 18:49:37 2015 -0700
@@ -847,11 +847,11 @@
/*
* Class: java_net_PlainSocketImpl
- * Method: socketSetOption
+ * Method: socketSetOption0
* Signature: (IZLjava/lang/Object;)V
*/
JNIEXPORT void JNICALL
-Java_java_net_PlainSocketImpl_socketSetOption(JNIEnv *env, jobject this,
+Java_java_net_PlainSocketImpl_socketSetOption0(JNIEnv *env, jobject this,
jint cmd, jboolean on,
jobject value) {
int fd;
--- a/jdk/src/java.base/unix/native/libnet/net_util_md.c Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/unix/native/libnet/net_util_md.c Thu Jun 04 18:49:37 2015 -0700
@@ -1023,12 +1023,10 @@
int i;
- /*
- * Different multicast options if IPv6 is enabled
- */
#ifdef AF_INET6
if (ipv6_available()) {
switch (cmd) {
+ // Different multicast options if IPv6 is enabled
case java_net_SocketOptions_IP_MULTICAST_IF:
case java_net_SocketOptions_IP_MULTICAST_IF2:
*level = IPPROTO_IPV6;
@@ -1039,6 +1037,13 @@
*level = IPPROTO_IPV6;
*optname = IPV6_MULTICAST_LOOP;
return 0;
+#if (defined(__solaris__) || defined(MACOSX))
+ // Map IP_TOS request to IPV6_TCLASS
+ case java_net_SocketOptions_IP_TOS:
+ *level = IPPROTO_IPV6;
+ *optname = IPV6_TCLASS;
+ return 0;
+#endif
}
}
#endif
@@ -1214,9 +1219,6 @@
* Wrapper for getsockopt system routine - does any necessary
* pre/post processing to deal with OS specific oddities :-
*
- * IP_TOS is a no-op with IPv6 sockets as it's setup when
- * the connection is established.
- *
* On Linux the SO_SNDBUF/SO_RCVBUF values must be post-processed
* to compensate for an incorrect value returned by the kernel.
*/
@@ -1227,21 +1229,6 @@
int rv;
socklen_t socklen = *len;
-#ifdef AF_INET6
- if ((level == IPPROTO_IP) && (opt == IP_TOS)) {
- if (ipv6_available()) {
-
- /*
- * For IPv6 socket option implemented at Java-level
- * so return -1.
- */
- int *tc = (int *)result;
- *tc = -1;
- return 0;
- }
- }
-#endif
-
rv = getsockopt(fd, level, opt, result, &socklen);
*len = socklen;
@@ -1285,8 +1272,7 @@
*
* For IP_TOS socket option need to mask off bits as this
* aren't automatically masked by the kernel and results in
- * an error. In addition IP_TOS is a NOOP with IPv6 as it
- * should be setup as connection time.
+ * an error.
*/
int
NET_SetSockOpt(int fd, int level, int opt, const void *arg,
@@ -1317,9 +1303,9 @@
/*
* IPPROTO/IP_TOS :-
- * 1. IPv6 on Solaris/Mac OS: NOOP and will be set
- * in flowinfo field when connecting TCP socket,
- * or sending UDP packet.
+ * 1. IPv6 on Solaris/Mac OS:
+ * Set the TOS OR Traffic Class value to cater for
+ * IPv6 and IPv4 scenarios.
* 2. IPv6 on Linux: By default Linux ignores flowinfo
* field so enable IPV6_FLOWINFO_SEND so that flowinfo
* will be examined. We also set the IPv4 TOS option in this case.
@@ -1329,12 +1315,6 @@
if (level == IPPROTO_IP && opt == IP_TOS) {
int *iptos;
-#if defined(AF_INET6) && (defined(__solaris__) || defined(MACOSX))
- if (ipv6_available()) {
- return 0;
- }
-#endif
-
#if defined(AF_INET6) && defined(__linux__)
if (ipv6_available()) {
int optval = 1;
@@ -1342,6 +1322,16 @@
(void *)&optval, sizeof(optval)) < 0) {
return -1;
}
+ /*
+ * Let's also set the IPV6_TCLASS flag.
+ * Linux appears to allow both IP_TOS and IPV6_TCLASS to be set
+ * This helps in mixed environments where IPv4 and IPv6 sockets
+ * are connecting.
+ */
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS,
+ arg, len) < 0) {
+ return -1;
+ }
}
#endif
@@ -1435,7 +1425,7 @@
* On Linux the receive buffer is used for both socket
* structures and the packet payload. The implication
* is that if SO_RCVBUF is too small then small packets
- * must be discard.
+ * must be discarded.
*/
#ifdef __linux__
if (level == SOL_SOCKET && opt == SO_RCVBUF) {
@@ -1619,7 +1609,7 @@
* NET_WAIT_READ, NET_WAIT_WRITE & NET_WAIT_CONNECT.
*
* The function will return when either the socket is ready for one
- * of the specified operation or the timeout expired.
+ * of the specified operations or the timeout expired.
*
* It returns the time left from the timeout (possibly 0), or -1 if it expired.
*/
--- a/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,10 +34,12 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.Override;
import java.lang.ProcessBuilder.Redirect;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -53,6 +55,9 @@
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
+ // Windows platforms support a forcible kill signal.
+ static final boolean SUPPORTS_NORMAL_TERMINATION = false;
+
/**
* Open a file for writing. If {@code append} is {@code true} then the file
* is opened for atomic append directly and a FileOutputStream constructed
@@ -306,7 +311,8 @@
}
- private long handle = 0;
+ private final long handle;
+ private final ProcessHandle processHandle;
private OutputStream stdin_stream;
private InputStream stdout_stream;
private InputStream stderr_stream;
@@ -385,6 +391,7 @@
handle = create(cmdstr, envblock, path,
stdHandles, redirectErrorStream);
+ processHandle = ProcessHandleImpl.getUnchecked(getProcessId0(handle));
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
@@ -481,7 +488,30 @@
private static native void waitForTimeoutInterruptibly(
long handle, long timeout);
- public void destroy() { terminateProcess(handle); }
+ @Override
+ public void destroy() {
+ terminateProcess(handle);
+ }
+
+ @Override
+ public CompletableFuture<Process> onExit() {
+ return ProcessHandleImpl.completion(getPid(), false)
+ .handleAsync((exitStatus, unusedThrowable) -> this);
+ }
+
+ @Override
+ public ProcessHandle toHandle() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("manageProcess"));
+ }
+ return processHandle;
+ }
+
+ @Override
+ public boolean supportsNormalTermination() {
+ return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
+ }
@Override
public Process destroyForcibly() {
@@ -493,8 +523,7 @@
@Override
public long getPid() {
- int pid = getProcessId0(handle);
- return pid;
+ return processHandle.getPid();
}
private static native int getProcessId0(long handle);
@@ -538,7 +567,7 @@
* Opens a file for atomic append. The file is created if it doesn't
* already exist.
*
- * @param file the file to open or create
+ * @param path the file to open or create
* @return the native HANDLE
*/
private static native long openForAtomicAppend(String path)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/windows/native/libjava/ProcessHandleImpl_win.c Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,434 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+#include "jni.h"
+#include "jvm.h"
+#include "jni_util.h"
+#include "java_lang_ProcessHandleImpl.h"
+#include "java_lang_ProcessHandleImpl_Info.h"
+
+#include <windows.h>
+#include <tlhelp32.h>
+#include <sddl.h>
+
+static void getStatInfo(JNIEnv *env, HANDLE handle, jobject jinfo);
+static void getCmdlineInfo(JNIEnv *env, HANDLE handle, jobject jinfo);
+static void procToUser(JNIEnv *env, HANDLE handle, jobject jinfo);
+
+/**************************************************************
+ * Implementation of ProcessHandleImpl_Info native methods.
+ */
+
+/* Field id for jString 'command' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_commandID;
+
+/* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_argumentsID;
+
+/* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_totalTimeID;
+
+/* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_startTimeID;
+
+/* Field id for jString 'accountName' in java.lang.ProcessHandleImpl.UserPrincipal */
+static jfieldID ProcessHandleImpl_Info_userID;
+
+/**************************************************************
+ * Static method to initialize field IDs.
+ *
+ * Class: java_lang_ProcessHandleImpl_Info
+ * Method: initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs
+ (JNIEnv *env, jclass clazz) {
+
+ CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env,
+ clazz, "command", "Ljava/lang/String;"));
+ CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env,
+ clazz, "arguments", "[Ljava/lang/String;"));
+ CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env,
+ clazz, "totalTime", "J"));
+ CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env,
+ clazz, "startTime", "J"));
+ CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env,
+ clazz, "user", "Ljava/lang/String;"));
+}
+
+/*
+ * Block until a child process exits and return its exit code.
+ */
+JNIEXPORT jint JNICALL
+Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env,
+ jclass junk,
+ jlong jpid,
+ jboolean reapStatus) {
+ DWORD pid = (DWORD)jpid;
+ DWORD exitValue = -1;
+ HANDLE handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION,
+ FALSE, pid);
+ if (handle == NULL) {
+ return exitValue; // No process with that pid is alive
+ }
+ do {
+ if (!GetExitCodeProcess(handle, &exitValue)) {
+ JNU_ThrowByNameWithLastError(env,
+ "java/lang/Runtime", "GetExitCodeProcess");
+ break;
+ }
+ if (exitValue == STILL_ACTIVE) {
+ HANDLE events[2];
+ events[0] = handle;
+ events[1] = JVM_GetThreadInterruptEvent();
+
+ if (WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events,
+ FALSE, /* Wait for ANY event */
+ INFINITE) /* Wait forever */
+ == WAIT_FAILED) {
+ JNU_ThrowByNameWithLastError(env,
+ "java/lang/Runtime", "WaitForMultipleObjects");
+ break;
+ }
+ }
+ } while (exitValue == STILL_ACTIVE);
+ CloseHandle(handle); // Ignore return code
+ return exitValue;
+}
+
+/*
+ * Returns the pid of the caller.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: getCurrentPid0
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_getCurrentPid0
+(JNIEnv *env, jclass clazz) {
+ DWORD pid = GetCurrentProcessId();
+ return (jlong)pid;
+}
+
+/*
+ * Returns the parent pid of the requested pid.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: parent0
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0
+(JNIEnv *env, jclass clazz, jlong jpid) {
+
+ DWORD ppid = -1;
+ DWORD wpid = (DWORD)jpid;
+ PROCESSENTRY32 pe32;
+ HANDLE hProcessSnap;
+
+ // Take a snapshot of all processes in the system.
+ hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (hProcessSnap == INVALID_HANDLE_VALUE) {
+ JNU_ThrowByName(env,
+ "java/lang/RuntimeException", "snapshot not available");
+ return -1;
+ }
+
+ // Retrieve information about the first process,
+ pe32.dwSize = sizeof (PROCESSENTRY32);
+ if (Process32First(hProcessSnap, &pe32)) {
+ // Now walk the snapshot of processes, and
+ do {
+ if (wpid == pe32.th32ProcessID) {
+ ppid = pe32.th32ParentProcessID;
+ break;
+ }
+ } while (Process32Next(hProcessSnap, &pe32));
+ } else {
+ JNU_ThrowByName(env,
+ "java/lang/RuntimeException", "snapshot not available");
+ return -1;
+ }
+ CloseHandle(hProcessSnap); // Ignore return code
+ return (jlong)ppid;
+}
+
+/*
+ * Returns the children of the requested pid and optionally each parent.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: getChildPids
+ * Signature: (J[J[J)I
+ */
+JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0
+(JNIEnv *env, jclass clazz, jlong jpid,
+ jlongArray jarray, jlongArray jparentArray) {
+
+ HANDLE hProcessSnap;
+ PROCESSENTRY32 pe32;
+ DWORD ppid = (DWORD)jpid;
+ size_t count = 0;
+ jlong* pids = NULL;
+ jlong* ppids = NULL;
+ size_t parentArraySize = 0;
+ size_t arraySize = 0;
+
+ arraySize = (*env)->GetArrayLength(env, jarray);
+ JNU_CHECK_EXCEPTION_RETURN(env, -1);
+ if (jparentArray != NULL) {
+ parentArraySize = (*env)->GetArrayLength(env, jparentArray);
+ JNU_CHECK_EXCEPTION_RETURN(env, -1);
+
+ if (arraySize != parentArraySize) {
+ JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
+ return 0;
+ }
+ }
+
+ // Take a snapshot of all processes in the system.
+ hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (hProcessSnap == INVALID_HANDLE_VALUE) {
+ JNU_ThrowByName(env,
+ "java/lang/RuntimeException", "snapshot not available");
+ return 0;
+ }
+
+ // Retrieve information about the first process,
+ pe32.dwSize = sizeof (PROCESSENTRY32);
+ if (Process32First(hProcessSnap, &pe32)) {
+ do { // Block to break out of on Exception
+ pids = (*env)->GetLongArrayElements(env, jarray, NULL);
+ if (pids == NULL) {
+ break;
+ }
+ if (jparentArray != NULL) {
+ ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL);
+ if (ppids == NULL) {
+ break;
+ }
+ }
+ // Now walk the snapshot of processes, and
+ // save information about each process in turn
+ do {
+ if (ppid == 0 ||
+ (pe32.th32ParentProcessID > 0
+ && (pe32.th32ParentProcessID == ppid))) {
+ if (count < arraySize) {
+ // Only store if it fits
+ pids[count] = (jlong)pe32.th32ProcessID;
+ if (ppids != NULL) {
+ // Store the parentPid
+ ppids[count] = (jlong) pe32.th32ParentProcessID;
+ }
+ }
+ count++; // Count to tabulate size needed
+ }
+ } while (Process32Next(hProcessSnap, &pe32));
+ } while (0);
+
+ if (pids != NULL) {
+ (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
+ }
+ if (ppids != NULL) {
+ (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
+ }
+ } else {
+ JNU_ThrowByName(env,
+ "java/lang/RuntimeException", "snapshot not available");
+ return 0;
+ }
+ CloseHandle(hProcessSnap);
+ // If more pids than array had size for; count will be greater than array size
+ return (jint)count;
+}
+
+/*
+ * Destroy the process.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: destroy0
+ * Signature: (Z)V
+ */
+JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_destroy0
+(JNIEnv *env, jclass clazz, jlong jpid, jboolean force) {
+ DWORD pid = (DWORD)jpid;
+ HANDLE handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
+ if (handle != NULL) {
+ TerminateProcess(handle, 1);
+ CloseHandle(handle); // Ignore return code
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+/*
+ * Class: java_lang_ProcessHandleImpl
+ * Method: isAlive0
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_isAlive0
+(JNIEnv *env, jclass clazz, jlong jpid) {
+ DWORD pid = (DWORD)jpid;
+
+ jboolean ret = JNI_FALSE;
+ HANDLE handle =
+ OpenProcess(THREAD_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION,
+ FALSE, pid);
+ if (handle != NULL) {
+ DWORD dwExitStatus;
+
+ GetExitCodeProcess(handle, &dwExitStatus);
+ CloseHandle(handle); // Ignore return code
+ ret = (dwExitStatus == STILL_ACTIVE);
+ }
+ return ret;
+}
+
+/**
+ * Assemble a 64 bit value from two 32 bit values.
+ */
+static jlong jlong_from(jint high, jint low) {
+ jlong result = 0;
+ result = ((jlong)high << 32) | ((0x000000000ffffffff) & (jlong)low);
+ return result;
+}
+
+/*
+ * Fill in the Info object from the OS information about the process.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: info0
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0
+ (JNIEnv *env, jobject jinfo, jlong jpid) {
+ DWORD pid = (DWORD)jpid;
+ int ret = 0;
+ HANDLE handle =
+ OpenProcess(THREAD_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION,
+ FALSE, pid);
+ if (handle == NULL) {
+ return;
+ }
+ getStatInfo(env, handle, jinfo);
+ getCmdlineInfo(env, handle, jinfo);
+ procToUser(env, handle, jinfo);
+
+ CloseHandle(handle); // Ignore return code
+}
+
+/**
+ * Read /proc/<pid>/stat and fill in the fields of the Info object.
+ * The executable name, plus the user, system, and start times are gathered.
+ */
+static void getStatInfo(JNIEnv *env, HANDLE handle, jobject jinfo) {
+ FILETIME CreationTime;
+ FILETIME ExitTime;
+ FILETIME KernelTime;
+ FILETIME UserTime;
+ jlong userTime; // nanoseconds
+ jlong totalTime; // nanoseconds
+ jlong startTime; // nanoseconds
+ UserTime.dwHighDateTime = 0;
+ UserTime.dwLowDateTime = 0;
+ KernelTime.dwHighDateTime = 0;
+ KernelTime.dwLowDateTime = 0;
+ CreationTime.dwHighDateTime = 0;
+ CreationTime.dwLowDateTime = 0;
+
+ if (GetProcessTimes(handle, &CreationTime, &ExitTime, &KernelTime, &UserTime)) {
+ userTime = jlong_from(UserTime.dwHighDateTime, UserTime.dwLowDateTime);
+ totalTime = jlong_from( KernelTime.dwHighDateTime, KernelTime.dwLowDateTime);
+ totalTime = (totalTime + userTime) * 100; // convert sum to nano-seconds
+
+ startTime = jlong_from(CreationTime.dwHighDateTime,
+ CreationTime.dwLowDateTime) / 10000;
+ startTime -= 11644473600000L; // Rebase Epoch from 1601 to 1970
+
+ (*env)->SetLongField(env, jinfo,
+ ProcessHandleImpl_Info_totalTimeID, totalTime);
+ JNU_CHECK_EXCEPTION(env);
+ (*env)->SetLongField(env, jinfo,
+ ProcessHandleImpl_Info_startTimeID, startTime);
+ JNU_CHECK_EXCEPTION(env);
+ }
+}
+
+static void getCmdlineInfo(JNIEnv *env, HANDLE handle, jobject jinfo) {
+ char exeName[1024];
+ int bufsize = sizeof exeName;
+ jstring commandObj;
+
+ if (QueryFullProcessImageName(handle, 0, exeName, &bufsize)) {
+ commandObj = (*env)->NewStringUTF(env, exeName);
+ CHECK_NULL(commandObj);
+ (*env)->SetObjectField(env, jinfo,
+ ProcessHandleImpl_Info_commandID, commandObj);
+ }
+}
+
+static void procToUser(JNIEnv *env, HANDLE handle, jobject jinfo) {
+#define TOKEN_LEN 256
+ DWORD token_len = TOKEN_LEN;
+ char token_buf[TOKEN_LEN];
+ TOKEN_USER *token_user = (TOKEN_USER*)token_buf;
+ HANDLE tokenHandle;
+ WCHAR domain[255 + 1 + 255 + 1]; // large enough to concat with '/' and name
+ WCHAR name[255 + 1];
+ DWORD domainLen = sizeof(domain) - sizeof(name);
+ DWORD nameLen = sizeof(name);
+ SID_NAME_USE use;
+ jstring s;
+ int ret;
+
+ if (!OpenProcessToken(handle, TOKEN_READ, &tokenHandle)) {
+ return;
+ }
+
+ ret = GetTokenInformation(tokenHandle, TokenUser, token_user,
+ token_len, &token_len);
+ CloseHandle(tokenHandle); // always close handle
+ if (!ret) {
+ JNU_ThrowByNameWithLastError(env,
+ "java/lang/RuntimeException", "GetTokenInformation");
+ return;
+ }
+
+ if (LookupAccountSidW(NULL, token_user->User.Sid, &name[0], &nameLen,
+ &domain[0], &domainLen, &use) == 0) {
+ // Name not available, convert to a String
+ LPWSTR str;
+ if (ConvertSidToStringSidW(token_user->User.Sid, &str) == 0) {
+ return;
+ }
+ s = (*env)->NewString(env, (const jchar *)str, (jsize)wcslen(str));
+ LocalFree(str);
+ } else {
+ wcscat(domain, L"\\");
+ wcscat(domain, name);
+ s = (*env)->NewString(env, (const jchar *)domain, (jsize)wcslen(domain));
+ }
+ CHECK_NULL(s);
+ (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, s);
+}
--- a/jdk/src/java.base/windows/native/libjli/java_md.c Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.base/windows/native/libjli/java_md.c Thu Jun 04 18:49:37 2015 -0700
@@ -44,7 +44,6 @@
/*
* Prototypes.
*/
-static jboolean GetPublicJREHome(char *path, jint pathsize);
static jboolean GetJVMPath(const char *jrepath, const char *jvmtype,
char *jvmpath, jint jvmpathsize);
static jboolean GetJREPath(char *path, jint pathsize);
@@ -334,12 +333,6 @@
}
}
- /* Look for a public JRE on this machine. */
- if (GetPublicJREHome(path, pathsize)) {
- JLI_TraceLauncher("JRE path is %s\n", path);
- return JNI_TRUE;
- }
-
JLI_ReportErrorMessage(JRE_ERROR8 JAVA_DLL);
return JNI_FALSE;
@@ -426,89 +419,6 @@
}
/*
- * Helpers to look in the registry for a public JRE.
- */
- /* Same for 1.5.0, 1.5.1, 1.5.2 etc. */
-#define JRE_KEY "Software\\JavaSoft\\Java Runtime Environment"
-
-static jboolean
-GetStringFromRegistry(HKEY key, const char *name, char *buf, jint bufsize)
-{
- DWORD type, size;
-
- if (RegQueryValueEx(key, name, 0, &type, 0, &size) == 0
- && type == REG_SZ
- && (size < (unsigned int)bufsize)) {
- if (RegQueryValueEx(key, name, 0, 0, buf, &size) == 0) {
- return JNI_TRUE;
- }
- }
- return JNI_FALSE;
-}
-
-static jboolean
-GetPublicJREHome(char *buf, jint bufsize)
-{
- HKEY key, subkey;
- char version[MAXPATHLEN];
-
- /*
- * Note: There is a very similar implementation of the following
- * registry reading code in the Windows java control panel (javacp.cpl).
- * If there are bugs here, a similar bug probably exists there. Hence,
- * changes here require inspection there.
- */
-
- /* Find the current version of the JRE */
- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, JRE_KEY, 0, KEY_READ, &key) != 0) {
- JLI_ReportErrorMessage(REG_ERROR1, JRE_KEY);
- return JNI_FALSE;
- }
-
- if (!GetStringFromRegistry(key, "CurrentVersion",
- version, sizeof(version))) {
- JLI_ReportErrorMessage(REG_ERROR2, JRE_KEY);
- RegCloseKey(key);
- return JNI_FALSE;
- }
-
- if (JLI_StrCmp(version, GetDotVersion()) != 0) {
- JLI_ReportErrorMessage(REG_ERROR3, JRE_KEY, version, GetDotVersion()
- );
- RegCloseKey(key);
- return JNI_FALSE;
- }
-
- /* Find directory where the current version is installed. */
- if (RegOpenKeyEx(key, version, 0, KEY_READ, &subkey) != 0) {
- JLI_ReportErrorMessage(REG_ERROR1, JRE_KEY, version);
- RegCloseKey(key);
- return JNI_FALSE;
- }
-
- if (!GetStringFromRegistry(subkey, "JavaHome", buf, bufsize)) {
- JLI_ReportErrorMessage(REG_ERROR4, JRE_KEY, version);
- RegCloseKey(key);
- RegCloseKey(subkey);
- return JNI_FALSE;
- }
-
- if (JLI_IsTraceLauncher()) {
- char micro[MAXPATHLEN];
- if (!GetStringFromRegistry(subkey, "MicroVersion", micro,
- sizeof(micro))) {
- printf("Warning: Can't read MicroVersion\n");
- micro[0] = '\0';
- }
- printf("Version major.minor.micro = %s.%s\n", version, micro);
- }
-
- RegCloseKey(key);
- RegCloseKey(subkey);
- return JNI_TRUE;
-}
-
-/*
* Support for doing cheap, accurate interval timing.
*/
static jboolean counterAvailable = JNI_FALSE;
--- a/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaComboBoxPopup.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaComboBoxPopup.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -204,7 +204,8 @@
if ((p.x + comboBoxBounds.width < 0) || (p.y + comboBoxBounds.height < 0) || (p.x > scrSize.width) || (p.y > scrSize.height)) {
return null;
}
- return new Rectangle(0, 22, scrSize.width, scrSize.height - 22);
+ Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(comboBox.getGraphicsConfiguration());
+ return new Rectangle(0, insets.top, scrSize.width, scrSize.height - insets.top - insets.bottom);
}
for (final GraphicsDevice gd : gs) {
@@ -314,10 +315,17 @@
}
final Rectangle r = new Rectangle(px, py, pw, ph);
- // Check whether it goes below the bottom of the screen, if so flip it
- if (r.y + r.height < top.y + scrBounds.y + scrBounds.height) return r;
-
- return new Rectangle(px, -r.height + comboBoxInsets.top, r.width, r.height);
+ if (py + ph > scrBounds.y + scrBounds.height) {
+ if (ph <= -scrBounds.y ) {
+ // popup goes above
+ r.y = -ph ;
+ } else {
+ // a full screen height popup
+ r.y = scrBounds.y + Math.max(0, (scrBounds.height - ph) / 2 );
+ r.height = Math.min(scrBounds.height, ph);
+ }
+ }
+ return r;
}
// The one to use when itemCount <= maxRowCount. Size never adjusts for arrows
--- a/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaSpinnerUI.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaSpinnerUI.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -232,12 +232,12 @@
editor.setInheritsPopupMenu(true);
if (editor.getFont() instanceof UIResource) {
- editor.setFont(spinner.getFont());
+ editor.setFont(new FontUIResource(spinner.getFont()));
}
final JFormattedTextField editorTextField = ((DefaultEditor)editor).getTextField();
if (editorTextField.getFont() instanceof UIResource) {
- editorTextField.setFont(spinner.getFont());
+ editorTextField.setFont(new FontUIResource(spinner.getFont()));
}
final InputMap spinnerInputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
final InputMap editorInputMap = editorTextField.getInputMap();
@@ -648,12 +648,12 @@
ui.updateToolTipTextForChildren(spinner);
} else if ("font".equals(propertyName)) {
JComponent editor = spinner.getEditor();
- if (editor != null && editor instanceof JSpinner.DefaultEditor) {
+ if (editor instanceof JSpinner.DefaultEditor) {
JTextField tf =
((JSpinner.DefaultEditor) editor).getTextField();
if (tf != null) {
if (tf.getFont() instanceof UIResource) {
- tf.setFont(spinner.getFont());
+ tf.setFont(new FontUIResource(spinner.getFont()));
}
}
}
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWMouseInfoPeer.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWMouseInfoPeer.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -51,8 +51,12 @@
return false;
}
- final Object windowPeer = AWTAccessor.getComponentAccessor().getPeer(w);
- return LWWindowPeer.getWindowUnderCursor() == windowPeer;
+ LWWindowPeer windowPeer = AWTAccessor.getComponentAccessor().getPeer(w);
+ if (windowPeer == null) {
+ return false;
+ }
+
+ return LWToolkit.getLWToolkit().getPlatformWindowUnderMouse() == windowPeer.getPlatformWindow();
}
}
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWTextAreaPeer.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWTextAreaPeer.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,6 @@
* questions.
*/
-
package sun.lwawt;
import java.awt.Component;
@@ -40,7 +39,6 @@
import javax.swing.JTextArea;
import javax.swing.ScrollPaneConstants;
import javax.swing.text.Document;
-import javax.swing.text.JTextComponent;
/**
* Lightweight implementation of {@link TextAreaPeer}. Delegates most of the
@@ -75,12 +73,13 @@
super.initializeImpl();
final int visibility = getTarget().getScrollbarVisibility();
synchronized (getDelegateLock()) {
+ getTextComponent().setWrapStyleWord(true);
setScrollBarVisibility(visibility);
}
}
@Override
- JTextComponent getTextComponent() {
+ JTextArea getTextComponent() {
return getDelegate().getView();
}
@@ -165,7 +164,7 @@
// JTextArea.replaceRange() is called.
final Document document = getTextComponent().getDocument();
document.removeDocumentListener(this);
- getDelegate().getView().replaceRange(text, start, end);
+ getTextComponent().replaceRange(text, start, end);
revalidate();
postEvent(new TextEvent(getTarget(), TextEvent.TEXT_VALUE_CHANGED));
document.addDocumentListener(this);
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -396,6 +396,8 @@
return new LWMouseInfoPeer();
}
+ protected abstract PlatformWindow getPlatformWindowUnderMouse();
+
@Override
public final PrintJob getPrintJob(Frame frame, String doctitle,
Properties props) {
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -61,6 +61,7 @@
private static final int MINIMUM_HEIGHT = 1;
private Insets insets = new Insets(0, 0, 0, 0);
+ private Rectangle maximizedBounds;
private GraphicsDevice graphicsDevice;
private GraphicsConfiguration graphicsConfig;
@@ -176,8 +177,10 @@
if (getTarget() instanceof Frame) {
- setTitle(((Frame) getTarget()).getTitle());
- setState(((Frame) getTarget()).getExtendedState());
+ Frame frame = (Frame) getTarget();
+ setTitle(frame.getTitle());
+ setState(frame.getExtendedState());
+ setMaximizedBounds(frame.getMaximizedBounds());
} else if (getTarget() instanceof Dialog) {
setTitle(((Dialog) getTarget()).getTitle());
}
@@ -543,9 +546,40 @@
return windowState;
}
+ private boolean isMaximizedBoundsSet() {
+ synchronized (getStateLock()) {
+ return maximizedBounds != null;
+ }
+ }
+
+ private Rectangle getDefaultMaximizedBounds() {
+ GraphicsConfiguration config = getGraphicsConfiguration();
+ Insets screenInsets = ((CGraphicsDevice) config.getDevice())
+ .getScreenInsets();
+ Rectangle gcBounds = config.getBounds();
+ return new Rectangle(
+ gcBounds.x + screenInsets.left,
+ gcBounds.y + screenInsets.top,
+ gcBounds.width - screenInsets.left - screenInsets.right,
+ gcBounds.height - screenInsets.top - screenInsets.bottom);
+ }
+
@Override
public void setMaximizedBounds(Rectangle bounds) {
- // TODO: not implemented
+ boolean isMaximizedBoundsSet;
+ synchronized (getStateLock()) {
+ this.maximizedBounds = (isMaximizedBoundsSet = (bounds != null))
+ ? constrainBounds(bounds) : null;
+ }
+
+ setPlatformMaximizedBounds(isMaximizedBoundsSet ? maximizedBounds
+ : getDefaultMaximizedBounds());
+ }
+
+ private void setPlatformMaximizedBounds(Rectangle bounds) {
+ platformWindow.setMaximizedBounds(
+ bounds.x, bounds.y,
+ bounds.width, bounds.height);
}
@Override
@@ -635,6 +669,10 @@
// Second, update the graphics config and surface data
final boolean isNewDevice = updateGraphicsDevice();
+ if (isNewDevice && !isMaximizedBoundsSet()) {
+ setPlatformMaximizedBounds(getDefaultMaximizedBounds());
+ }
+
if (resized || isNewDevice) {
replaceSurfaceData();
updateMinimumSize();
@@ -749,11 +787,10 @@
lastMouseEventPeer = targetPeer;
}
} else {
- PlatformWindow topmostPlatforWindow =
- platformWindow.getTopmostPlatformWindowUnderMouse();
+ PlatformWindow topmostPlatformWindow = LWToolkit.getLWToolkit().getPlatformWindowUnderMouse();
LWWindowPeer topmostWindowPeer =
- topmostPlatforWindow != null ? topmostPlatforWindow.getPeer() : null;
+ topmostPlatformWindow != null ? topmostPlatformWindow.getPeer() : null;
// topmostWindowPeer == null condition is added for the backward
// compatibility with applets. It can be removed when the
@@ -764,8 +801,7 @@
screenX, screenY, modifiers, clickCount, popupTrigger,
targetPeer);
} else {
- LWComponentPeer<?, ?> topmostTargetPeer =
- topmostWindowPeer != null ? topmostWindowPeer.findPeerAt(r.x + x, r.y + y) : null;
+ LWComponentPeer<?, ?> topmostTargetPeer = topmostWindowPeer.findPeerAt(r.x + x, r.y + y);
topmostWindowPeer.generateMouseEnterExitEventsForComponents(when, button, x, y,
screenX, screenY, modifiers, clickCount, popupTrigger,
topmostTargetPeer);
@@ -1057,6 +1093,9 @@
public final void displayChanged() {
if (updateGraphicsDevice()) {
updateMinimumSize();
+ if (!isMaximizedBoundsSet()) {
+ setPlatformMaximizedBounds(getDefaultMaximizedBounds());
+ }
}
// Replace surface unconditionally, because internal state of the
// GraphicsDevice could be changed.
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/PlatformWindow.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/PlatformWindow.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -67,6 +67,11 @@
public void setBounds(int x, int y, int w, int h);
/*
+ * Sets the maximized bounds.
+ */
+ public default void setMaximizedBounds(int x, int y, int w, int h){}
+
+ /*
* Returns the graphics device where the window is.
*/
public GraphicsDevice getGraphicsDevice();
@@ -107,8 +112,6 @@
public void setAlwaysOnTop(boolean value);
- public PlatformWindow getTopmostPlatformWindowUnderMouse();
-
public void updateFocusableWindowState();
public boolean rejectFocusRequest(CausedFocusEvent.Cause cause);
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -630,7 +630,7 @@
if (!allowIgnored) {
final AccessibleRole role = context.getAccessibleRole();
- if (role != null && ignoredRoles.contains(roleKey(role))) {
+ if (role != null && ignoredRoles != null && ignoredRoles.contains(roleKey(role))) {
// Get the child's unignored children.
_addChildren(child, whichChildren, false, childrenAndRoles);
} else {
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -129,11 +129,6 @@
@Override
public void setAlwaysOnTop(boolean value) {}
- // This method should be properly implemented for applets.
- // It returns null just as a stub.
- @Override
- public PlatformWindow getTopmostPlatformWindowUnderMouse() { return null; }
-
@Override
public void updateFocusableWindowState() {}
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformLWWindow.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformLWWindow.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -162,11 +162,6 @@
}
@Override
- public PlatformWindow getTopmostPlatformWindowUnderMouse(){
- return null;
- }
-
- @Override
public void setOpacity(float opacity) {
}
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java Thu Jun 04 18:49:37 2015 -0700
@@ -51,6 +51,8 @@
private static native void nativeSetNSWindowMenuBar(long nsWindowPtr, long menuBarPtr);
private static native Insets nativeGetNSWindowInsets(long nsWindowPtr);
private static native void nativeSetNSWindowBounds(long nsWindowPtr, double x, double y, double w, double h);
+ private static native void nativeSetNSWindowStandardFrame(long nsWindowPtr,
+ double x, double y, double w, double h);
private static native void nativeSetNSWindowMinMax(long nsWindowPtr, double minW, double minH, double maxW, double maxH);
private static native void nativePushNSWindowToBack(long nsWindowPtr);
private static native void nativePushNSWindowToFront(long nsWindowPtr);
@@ -61,9 +63,9 @@
private static native void nativeSetEnabled(long nsWindowPtr, boolean isEnabled);
private static native void nativeSynthesizeMouseEnteredExitedEvents();
private static native void nativeDispose(long nsWindowPtr);
- private static native CPlatformWindow nativeGetTopmostPlatformWindowUnderMouse();
private static native void nativeEnterFullScreenMode(long nsWindowPtr);
private static native void nativeExitFullScreenMode(long nsWindowPtr);
+ static native CPlatformWindow nativeGetTopmostPlatformWindowUnderMouse();
// Loger to report issues happened during execution but that do not affect functionality
private static final PlatformLogger logger = PlatformLogger.getLogger("sun.lwawt.macosx.CPlatformWindow");
@@ -474,6 +476,10 @@
nativeSetNSWindowBounds(getNSWindowPtr(), x, y, w, h);
}
+ public void setMaximizedBounds(int x, int y, int w, int h) {
+ nativeSetNSWindowStandardFrame(getNSWindowPtr(), x, y, w, h);
+ }
+
private boolean isMaximized() {
return undecorated ? this.normalBounds != null
: CWrapper.NSWindow.isZoomed(getNSWindowPtr());
@@ -750,10 +756,6 @@
setStyleBits(ALWAYS_ON_TOP, isAlwaysOnTop);
}
- public PlatformWindow getTopmostPlatformWindowUnderMouse(){
- return CPlatformWindow.nativeGetTopmostPlatformWindowUnderMouse();
- }
-
@Override
public void setOpacity(float opacity) {
CWrapper.NSWindow.setAlphaValue(getNSWindowPtr(), opacity);
@@ -983,13 +985,11 @@
}
private void checkZoom() {
- if (target instanceof Frame && isVisible()) {
- Frame targetFrame = (Frame)target;
- if (targetFrame.getExtendedState() != Frame.MAXIMIZED_BOTH && isMaximized()) {
- deliverZoom(true);
- } else if (targetFrame.getExtendedState() == Frame.MAXIMIZED_BOTH && !isMaximized()) {
- deliverZoom(false);
- }
+ int state = peer.getState();
+ if (state != Frame.MAXIMIZED_BOTH && isMaximized()) {
+ deliverZoom(true);
+ } else if (state == Frame.MAXIMIZED_BOTH && !isMaximized()) {
+ deliverZoom(false);
}
}
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -78,7 +78,7 @@
@Override
public void mousePress(int buttons) {
mouseButtonsState |= buttons;
-
+ checkMousePos();
mouseEvent(fDevice.getCGDisplayID(), mouseLastX, mouseLastY,
buttons, true, false);
}
@@ -92,11 +92,40 @@
@Override
public void mouseRelease(int buttons) {
mouseButtonsState &= ~buttons;
-
+ checkMousePos();
mouseEvent(fDevice.getCGDisplayID(), mouseLastX, mouseLastY,
buttons, false, false);
}
+ /**
+ * Set unknown mouse location, if needed.
+ */
+ private void checkMousePos() {
+ if (mouseLastX == MOUSE_LOCATION_UNKNOWN ||
+ mouseLastY == MOUSE_LOCATION_UNKNOWN) {
+
+ Rectangle deviceBounds = fDevice.getDefaultConfiguration().getBounds();
+ Point mousePos = CCursorManager.getInstance().getCursorPosition();
+
+ if (mousePos.x < deviceBounds.x) {
+ mousePos.x = deviceBounds.x;
+ }
+ else if (mousePos.x > deviceBounds.x + deviceBounds.width) {
+ mousePos.x = deviceBounds.x + deviceBounds.width;
+ }
+
+ if (mousePos.y < deviceBounds.y) {
+ mousePos.y = deviceBounds.y;
+ }
+ else if (mousePos.y > deviceBounds.y + deviceBounds.height) {
+ mousePos.y = deviceBounds.y + deviceBounds.height;
+ }
+
+ mouseLastX = mousePos.x;
+ mouseLastY = mousePos.y;
+ }
+ }
+
@Override
public native void mouseWheel(int wheelAmt);
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CViewPlatformEmbeddedFrame.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CViewPlatformEmbeddedFrame.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -144,11 +144,6 @@
}
@Override
- public PlatformWindow getTopmostPlatformWindowUnderMouse() {
- return null;
- }
-
- @Override
public void updateFocusableWindowState() {
}
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java Thu Jun 04 18:49:37 2015 -0700
@@ -929,4 +929,9 @@
!path.endsWith("/") &&
!path.endsWith(".");
}
+
+ @Override
+ protected PlatformWindow getPlatformWindowUnderMouse() {
+ return CPlatformWindow.nativeGetTopmostPlatformWindowUnderMouse();
+ }
}
--- a/jdk/src/java.desktop/macosx/native/include/jawt_md.h Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/native/include/jawt_md.h Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2013 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
--- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.h Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.h Thu Jun 04 18:49:37 2015 -0700
@@ -46,6 +46,7 @@
NSWindow *nsWindow;
AWTWindow *ownerWindow;
jint preFullScreenLevel;
+ NSRect standardFrame;
}
// An instance of either AWTWindow_Normal or AWTWindow_Panel
@@ -59,7 +60,7 @@
@property (nonatomic) jint styleBits;
@property (nonatomic) BOOL isEnabled;
@property (nonatomic) jint preFullScreenLevel;
-
+@property (nonatomic) NSRect standardFrame;
- (id) initWithPlatformWindow:(JNFWeakJObjectWrapper *)javaPlatformWindow
ownerWindow:owner
--- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -184,6 +184,7 @@
@synthesize isEnabled;
@synthesize ownerWindow;
@synthesize preFullScreenLevel;
+@synthesize standardFrame;
- (void) updateMinMaxSize:(BOOL)resizable {
if (resizable) {
@@ -509,6 +510,12 @@
// window exposing in _setVisible:(BOOL)
}
+- (NSRect)windowWillUseStandardFrame:(NSWindow *)window
+ defaultFrame:(NSRect)newFrame {
+
+ return [self standardFrame];
+}
+
- (void) _deliverIconify:(BOOL)iconify {
AWT_ASSERT_APPKIT_THREAD;
@@ -953,6 +960,30 @@
/*
* Class: sun_lwawt_macosx_CPlatformWindow
+ * Method: nativeSetNSWindowStandardFrame
+ * Signature: (JDDDD)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowStandardFrame
+(JNIEnv *env, jclass clazz, jlong windowPtr, jdouble originX, jdouble originY,
+ jdouble width, jdouble height)
+{
+ JNF_COCOA_ENTER(env);
+
+ NSRect jrect = NSMakeRect(originX, originY, width, height);
+
+ NSWindow *nsWindow = OBJC(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+
+ NSRect rect = ConvertNSScreenRect(NULL, jrect);
+ AWTWindow *window = (AWTWindow*)[nsWindow delegate];
+ window.standardFrame = rect;
+ }];
+
+ JNF_COCOA_EXIT(env);
+}
+
+/*
+ * Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativeSetNSWindowMinMax
* Signature: (JDDDD)V
*/
@@ -1131,15 +1162,16 @@
JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeGetTopmostPlatformWindowUnderMouse
(JNIEnv *env, jclass clazz)
{
- jobject topmostWindowUnderMouse = nil;
+ __block jobject topmostWindowUnderMouse = nil;
JNF_COCOA_ENTER(env);
- AWT_ASSERT_APPKIT_THREAD;
- AWTWindow *awtWindow = [AWTWindow getTopmostWindowUnderMouse];
- if (awtWindow != nil) {
- topmostWindowUnderMouse = [awtWindow.javaPlatformWindow jObject];
- }
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^{
+ AWTWindow *awtWindow = [AWTWindow getTopmostWindowUnderMouse];
+ if (awtWindow != nil) {
+ topmostWindowUnderMouse = [awtWindow.javaPlatformWindow jObject];
+ }
+ }];
JNF_COCOA_EXIT(env);
--- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CCursorManager.m Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CCursorManager.m Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -118,13 +118,11 @@
JNF_COCOA_ENTER(env);
- __block NSPoint pt = NSZeroPoint;
-
- [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
- pt = ConvertNSScreenPoint(env, [NSEvent mouseLocation]);
- }];
-
- jpt = NSToJavaPoint(env, pt);
+ CGEventRef event = CGEventCreate(NULL);
+ CGPoint globalPos = CGEventGetLocation(event);
+ CFRelease(event);
+
+ jpt = NSToJavaPoint(env, globalPos);
JNF_COCOA_EXIT(env);
--- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -146,47 +146,10 @@
// This is the native method called when Robot mouse events occur.
// The CRobot tracks the mouse position, and which button was
- // pressed. If the mouse position is unknown it is obtained from
- // CGEvents. The peer also tracks the mouse button desired state,
+ // pressed. The peer also tracks the mouse button desired state,
// the appropriate key modifier state, and whether the mouse action
// is simply a mouse move with no mouse button state changes.
- CGError err = kCGErrorSuccess;
-
- CGRect globalDeviceBounds = CGDisplayBounds(displayID);
-
- // Set unknown mouse location, if needed.
- if ((mouseLastX == sun_lwawt_macosx_CRobot_MOUSE_LOCATION_UNKNOWN) ||
- (mouseLastY == sun_lwawt_macosx_CRobot_MOUSE_LOCATION_UNKNOWN))
- {
- CGEventRef event = CGEventCreate(NULL);
- if (event == NULL) {
- return;
- }
-
- CGPoint globalPos = CGEventGetLocation(event);
- CFRelease(event);
-
- // Normalize the coords within this display device, as
- // per Robot rules.
- if (globalPos.x < CGRectGetMinX(globalDeviceBounds)) {
- globalPos.x = CGRectGetMinX(globalDeviceBounds);
- }
- else if (globalPos.x > CGRectGetMaxX(globalDeviceBounds)) {
- globalPos.x = CGRectGetMaxX(globalDeviceBounds);
- }
-
- if (globalPos.y < CGRectGetMinY(globalDeviceBounds)) {
- globalPos.y = CGRectGetMinY(globalDeviceBounds);
- }
- else if (globalPos.y > CGRectGetMaxY(globalDeviceBounds)) {
- globalPos.y = CGRectGetMaxY(globalDeviceBounds);
- }
-
- mouseLastX = (jint)globalPos.x;
- mouseLastY = (jint)globalPos.y;
- }
-
// volatile, otherwise it warns that it might be clobbered by 'longjmp'
volatile CGPoint point;
--- a/jdk/src/java.desktop/share/classes/com/sun/beans/decoder/ArrayElementHandler.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/beans/decoder/ArrayElementHandler.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2013 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -93,7 +93,9 @@
*/
static enum Settings {
GTK_FONT_NAME,
- GTK_ICON_SIZES
+ GTK_ICON_SIZES,
+ GTK_CURSOR_BLINK,
+ GTK_CURSOR_BLINK_TIME
}
/* Custom regions are needed for representing regions that don't exist
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -371,7 +371,17 @@
int vProgWidth = 22 - (progXThickness * 2);
int vProgHeight = 80 - (progYThickness * 2);
- Integer caretBlinkRate = Integer.valueOf(500);
+ Integer caretBlinkRate;
+ if (Boolean.FALSE.equals(GTKEngine.INSTANCE.getSetting(
+ GTKEngine.Settings.GTK_CURSOR_BLINK))) {
+ caretBlinkRate = Integer.valueOf(0);
+ } else {
+ caretBlinkRate = (Integer) GTKEngine.INSTANCE.getSetting(
+ GTKEngine.Settings.GTK_CURSOR_BLINK_TIME);
+ if (caretBlinkRate == null) {
+ caretBlinkRate = Integer.valueOf(500);
+ }
+ }
Insets zeroInsets = new InsetsUIResource(0, 0, 0, 0);
Double defaultCaretAspectRatio = new Double(0.025);
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk.properties Thu Jun 04 18:49:37 2015 -0700
@@ -34,9 +34,9 @@
FileChooser.newFolderNoDirectoryError.textAndMnemonic=Error creating directory "{0}": No such file or directory
FileChooser.deleteFileButton.textAndMnemonic=De&lete File
FileChooser.renameFileButton.textAndMnemonic=&Rename File
-FileChooser.cancelButton.textAndMnemonic=&Cancel
-FileChooser.saveButton.textAndMnemonic=&OK
-FileChooser.openButton.textAndMnemonic=&OK
+FileChooser.cancelButton.textAndMnemonic=Cancel
+FileChooser.saveButton.textAndMnemonic=OK
+FileChooser.openButton.textAndMnemonic=OK
FileChooser.saveDialogTitle.textAndMnemonic=Save
FileChooser.openDialogTitle.textAndMnemonic=Open
FileChooser.pathLabel.textAndMnemonic=&Selection:
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_de.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_de.properties Thu Jun 04 18:49:37 2015 -0700
@@ -34,9 +34,9 @@
FileChooser.newFolderNoDirectoryError.textAndMnemonic=Fehler beim Erstellen von Verzeichnis "{0}": Datei oder Verzeichnis nicht vorhanden
FileChooser.deleteFileButton.textAndMnemonic=Datei &l\u00F6schen
FileChooser.renameFileButton.textAndMnemonic=Datei &umbenennen
-FileChooser.cancelButton.textAndMnemonic=&Abbrechen
-FileChooser.saveButton.textAndMnemonic=&OK
-FileChooser.openButton.textAndMnemonic=&OK
+FileChooser.cancelButton.textAndMnemonic=Abbrechen
+FileChooser.saveButton.textAndMnemonic=OK
+FileChooser.openButton.textAndMnemonic=OK
FileChooser.saveDialogTitle.textAndMnemonic=Speichern
FileChooser.openDialogTitle.textAndMnemonic=\u00D6ffnen
FileChooser.pathLabel.textAndMnemonic=Aus&wahl:
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_es.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_es.properties Thu Jun 04 18:49:37 2015 -0700
@@ -34,9 +34,9 @@
FileChooser.newFolderNoDirectoryError.textAndMnemonic=Error al crear el directorio "{0}": no existe dicho archivo o directorio
FileChooser.deleteFileButton.textAndMnemonic=Su&primir Archivo
FileChooser.renameFileButton.textAndMnemonic=Cambiar Nomb&re de Archivo
-FileChooser.cancelButton.textAndMnemonic=&Cancelar
-FileChooser.saveButton.textAndMnemonic=&Aceptar
-FileChooser.openButton.textAndMnemonic=&Aceptar
+FileChooser.cancelButton.textAndMnemonic=Cancelar
+FileChooser.saveButton.textAndMnemonic=Aceptar
+FileChooser.openButton.textAndMnemonic=Aceptar
FileChooser.saveDialogTitle.textAndMnemonic=Guardar
FileChooser.openDialogTitle.textAndMnemonic=Abrir
FileChooser.pathLabel.textAndMnemonic=&Selecci\u00F3n:
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_fr.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_fr.properties Thu Jun 04 18:49:37 2015 -0700
@@ -34,9 +34,9 @@
FileChooser.newFolderNoDirectoryError.textAndMnemonic=Erreur lors de la cr\u00E9ation du r\u00E9pertoire "{0}" : ce fichier ou r\u00E9pertoire n''existe pas
FileChooser.deleteFileButton.textAndMnemonic=Supprimer &le fichier
FileChooser.renameFileButton.textAndMnemonic=&Renommer le fichier
-FileChooser.cancelButton.textAndMnemonic=&Annuler
-FileChooser.saveButton.textAndMnemonic=&OK
-FileChooser.openButton.textAndMnemonic=&OK
+FileChooser.cancelButton.textAndMnemonic=Annuler
+FileChooser.saveButton.textAndMnemonic=OK
+FileChooser.openButton.textAndMnemonic=OK
FileChooser.saveDialogTitle.textAndMnemonic=Enregistrer
FileChooser.openDialogTitle.textAndMnemonic=Ouvrir
FileChooser.pathLabel.textAndMnemonic=&S\u00E9lection :
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_it.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_it.properties Thu Jun 04 18:49:37 2015 -0700
@@ -34,9 +34,9 @@
FileChooser.newFolderNoDirectoryError.textAndMnemonic=Errore durante la creazione della directory "{0}": file o directory inesistente
FileChooser.deleteFileButton.textAndMnemonic=E&limina file
FileChooser.renameFileButton.textAndMnemonic=&Rinomina file
-FileChooser.cancelButton.textAndMnemonic=&Annulla
-FileChooser.saveButton.textAndMnemonic=&OK
-FileChooser.openButton.textAndMnemonic=&OK
+FileChooser.cancelButton.textAndMnemonic=Annulla
+FileChooser.saveButton.textAndMnemonic=OK
+FileChooser.openButton.textAndMnemonic=OK
FileChooser.saveDialogTitle.textAndMnemonic=Salva
FileChooser.openDialogTitle.textAndMnemonic=Apri
FileChooser.pathLabel.textAndMnemonic=&Selezione:
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_ja.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_ja.properties Thu Jun 04 18:49:37 2015 -0700
@@ -34,9 +34,9 @@
FileChooser.newFolderNoDirectoryError.textAndMnemonic=\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA"{0}"\u306E\u4F5C\u6210\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F: \u3053\u306E\u30D5\u30A1\u30A4\u30EB\u307E\u305F\u306F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u5B58\u5728\u3057\u307E\u305B\u3093
FileChooser.deleteFileButton.textAndMnemonic=\u30D5\u30A1\u30A4\u30EB\u306E\u524A\u9664(&L)
FileChooser.renameFileButton.textAndMnemonic=\u30D5\u30A1\u30A4\u30EB\u306E\u540D\u524D\u5909\u66F4(&R)
-FileChooser.cancelButton.textAndMnemonic=\u53D6\u6D88(&C)
-FileChooser.saveButton.textAndMnemonic=OK(&O)
-FileChooser.openButton.textAndMnemonic=OK(&O)
+FileChooser.cancelButton.textAndMnemonic=\u53D6\u6D88
+FileChooser.saveButton.textAndMnemonic=OK
+FileChooser.openButton.textAndMnemonic=OK
FileChooser.saveDialogTitle.textAndMnemonic=\u4FDD\u5B58
FileChooser.openDialogTitle.textAndMnemonic=\u958B\u304F
FileChooser.pathLabel.textAndMnemonic=\u9078\u629E(&S):
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_ko.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_ko.properties Thu Jun 04 18:49:37 2015 -0700
@@ -34,9 +34,9 @@
FileChooser.newFolderNoDirectoryError.textAndMnemonic="{0}" \uB514\uB809\uD1A0\uB9AC\uB97C \uC0DD\uC131\uD558\uB294 \uC911 \uC624\uB958 \uBC1C\uC0DD: \uD574\uB2F9 \uD30C\uC77C \uB610\uB294 \uB514\uB809\uD1A0\uB9AC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.
FileChooser.deleteFileButton.textAndMnemonic=\uD30C\uC77C \uC0AD\uC81C(&L)
FileChooser.renameFileButton.textAndMnemonic=\uD30C\uC77C \uC774\uB984 \uBC14\uAFB8\uAE30(&R)
-FileChooser.cancelButton.textAndMnemonic=\uCDE8\uC18C(&C)
-FileChooser.saveButton.textAndMnemonic=\uD655\uC778(&O)
-FileChooser.openButton.textAndMnemonic=\uD655\uC778(&O)
+FileChooser.cancelButton.textAndMnemonic=\uCDE8\uC18C
+FileChooser.saveButton.textAndMnemonic=\uD655\uC778
+FileChooser.openButton.textAndMnemonic=\uD655\uC778
FileChooser.saveDialogTitle.textAndMnemonic=\uC800\uC7A5
FileChooser.openDialogTitle.textAndMnemonic=\uC5F4\uAE30
FileChooser.pathLabel.textAndMnemonic=\uC120\uD0DD \uC0AC\uD56D(&S):
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_pt_BR.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_pt_BR.properties Thu Jun 04 18:49:37 2015 -0700
@@ -34,9 +34,9 @@
FileChooser.newFolderNoDirectoryError.textAndMnemonic=Erro ao criar o diret\u00F3rio "{0}": N\u00E3o h\u00E1 arquivo ou diret\u00F3rio
FileChooser.deleteFileButton.textAndMnemonic=De&letar Arquivo
FileChooser.renameFileButton.textAndMnemonic=&Renomear Arquivo
-FileChooser.cancelButton.textAndMnemonic=&Cancelar
-FileChooser.saveButton.textAndMnemonic=&OK
-FileChooser.openButton.textAndMnemonic=&OK
+FileChooser.cancelButton.textAndMnemonic=Cancelar
+FileChooser.saveButton.textAndMnemonic=OK
+FileChooser.openButton.textAndMnemonic=OK
FileChooser.saveDialogTitle.textAndMnemonic=Salvar
FileChooser.openDialogTitle.textAndMnemonic=Abrir
FileChooser.pathLabel.textAndMnemonic=&Sele\u00E7\u00E3o:
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_sv.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_sv.properties Thu Jun 04 18:49:37 2015 -0700
@@ -34,9 +34,9 @@
FileChooser.newFolderNoDirectoryError.textAndMnemonic=Ett fel intr\u00E4ffade vid f\u00F6rs\u00F6k att skapa katalogen "{0}": Filen eller katalogen finns inte
FileChooser.deleteFileButton.textAndMnemonic=Ta &bort fil
FileChooser.renameFileButton.textAndMnemonic=&\u00C4ndra namn p\u00E5 filen
-FileChooser.cancelButton.textAndMnemonic=&Avbryt
-FileChooser.saveButton.textAndMnemonic=&OK
-FileChooser.openButton.textAndMnemonic=&OK
+FileChooser.cancelButton.textAndMnemonic=Avbryt
+FileChooser.saveButton.textAndMnemonic=OK
+FileChooser.openButton.textAndMnemonic=OK
FileChooser.saveDialogTitle.textAndMnemonic=Spara
FileChooser.openDialogTitle.textAndMnemonic=\u00D6ppna
FileChooser.pathLabel.textAndMnemonic=&Urval:
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_zh_CN.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_zh_CN.properties Thu Jun 04 18:49:37 2015 -0700
@@ -34,9 +34,9 @@
FileChooser.newFolderNoDirectoryError.textAndMnemonic=\u521B\u5EFA\u76EE\u5F55 "{0}" \u65F6\u51FA\u9519: \u6CA1\u6709\u6B64\u7C7B\u6587\u4EF6\u6216\u76EE\u5F55
FileChooser.deleteFileButton.textAndMnemonic=\u5220\u9664\u6587\u4EF6(&L)
FileChooser.renameFileButton.textAndMnemonic=\u91CD\u547D\u540D\u6587\u4EF6(&R)
-FileChooser.cancelButton.textAndMnemonic=\u53D6\u6D88(&C)
-FileChooser.saveButton.textAndMnemonic=\u786E\u5B9A(&O)
-FileChooser.openButton.textAndMnemonic=\u786E\u5B9A(&O)
+FileChooser.cancelButton.textAndMnemonic=\u53D6\u6D88
+FileChooser.saveButton.textAndMnemonic=\u786E\u5B9A
+FileChooser.openButton.textAndMnemonic=\u786E\u5B9A
FileChooser.saveDialogTitle.textAndMnemonic=\u4FDD\u5B58
FileChooser.openDialogTitle.textAndMnemonic=\u6253\u5F00
FileChooser.pathLabel.textAndMnemonic=\u9009\u5B9A\u5185\u5BB9(&S):
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_zh_TW.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk_zh_TW.properties Thu Jun 04 18:49:37 2015 -0700
@@ -34,9 +34,9 @@
FileChooser.newFolderNoDirectoryError.textAndMnemonic=\u5EFA\u7ACB\u76EE\u9304 "{0}" \u6642\u767C\u751F\u932F\u8AA4: \u6C92\u6709\u6B64\u6A94\u6848\u6216\u76EE\u9304
FileChooser.deleteFileButton.textAndMnemonic=\u522A\u9664\u6A94\u6848(&L)
FileChooser.renameFileButton.textAndMnemonic=\u91CD\u65B0\u547D\u540D\u6A94\u6848(&R)
-FileChooser.cancelButton.textAndMnemonic=\u53D6\u6D88(&C)
-FileChooser.saveButton.textAndMnemonic=\u78BA\u5B9A(&O)
-FileChooser.openButton.textAndMnemonic=\u78BA\u5B9A(&O)
+FileChooser.cancelButton.textAndMnemonic=\u53D6\u6D88
+FileChooser.saveButton.textAndMnemonic=\u78BA\u5B9A
+FileChooser.openButton.textAndMnemonic=\u78BA\u5B9A
FileChooser.saveDialogTitle.textAndMnemonic=\u5132\u5B58
FileChooser.openDialogTitle.textAndMnemonic=\u958B\u555F
FileChooser.pathLabel.textAndMnemonic=\u9078\u53D6(&S):
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic.properties Thu Jun 04 18:49:37 2015 -0700
@@ -43,13 +43,13 @@
Specify a different file name.
FileChooser.acceptAllFileFilter.textAndMnemonic=All Files
FileChooser.cancelButton.textAndMnemonic=Cancel
-FileChooser.saveButton.textAndMnemonic=&Save
-FileChooser.openButton.textAndMnemonic=&Open
+FileChooser.saveButton.textAndMnemonic=Save
+FileChooser.openButton.textAndMnemonic=Open
FileChooser.saveDialogTitle.textAndMnemonic=Save
FileChooser.openDialogTitle.textAndMnemonic=Open
FileChooser.updateButton.textAndMnemonic=&Update
FileChooser.helpButton.textAndMnemonic=&Help
-FileChooser.directoryOpenButton.textAndMnemonic=&Open
+FileChooser.directoryOpenButton.textAndMnemonic=Open
# File Size Units
FileChooser.fileSizeKiloBytes={0} KB
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_de.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_de.properties Thu Jun 04 18:49:37 2015 -0700
@@ -42,13 +42,13 @@
FileChooser.renameErrorFileExists.textAndMnemonic={0} kann nicht umbenannt werden: Es ist bereits eine Datei mit dem angegebenen Namen vorhanden. Geben Sie einen anderen Dateinamen an.
FileChooser.acceptAllFileFilter.textAndMnemonic=Alle Dateien
FileChooser.cancelButton.textAndMnemonic=Abbrechen
-FileChooser.saveButton.textAndMnemonic=&Speichern
-FileChooser.openButton.textAndMnemonic=\u00D6&ffnen
+FileChooser.saveButton.textAndMnemonic=Speichern
+FileChooser.openButton.textAndMnemonic=\u00D6ffnen
FileChooser.saveDialogTitle.textAndMnemonic=Speichern
FileChooser.openDialogTitle.textAndMnemonic=\u00D6ffnen
FileChooser.updateButton.textAndMnemonic=A&ktualisieren
FileChooser.helpButton.textAndMnemonic=&Hilfe
-FileChooser.directoryOpenButton.textAndMnemonic=\u00D6&ffnen
+FileChooser.directoryOpenButton.textAndMnemonic=\u00D6ffnen
# File Size Units
FileChooser.fileSizeKiloBytes={0} KB
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_es.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_es.properties Thu Jun 04 18:49:37 2015 -0700
@@ -42,13 +42,13 @@
FileChooser.renameErrorFileExists.textAndMnemonic=No se puede cambiar el nombre de {0}: ya existe un archivo con el nombre especificado. Especifique otro nombre de archivo.
FileChooser.acceptAllFileFilter.textAndMnemonic=Todos los Archivos
FileChooser.cancelButton.textAndMnemonic=Cancelar
-FileChooser.saveButton.textAndMnemonic=&Guardar
-FileChooser.openButton.textAndMnemonic=&Abrir
+FileChooser.saveButton.textAndMnemonic=Guardar
+FileChooser.openButton.textAndMnemonic=Abrir
FileChooser.saveDialogTitle.textAndMnemonic=Guardar
FileChooser.openDialogTitle.textAndMnemonic=Abrir
FileChooser.updateButton.textAndMnemonic=Act&ualizar
FileChooser.helpButton.textAndMnemonic=A&yuda
-FileChooser.directoryOpenButton.textAndMnemonic=&Abrir
+FileChooser.directoryOpenButton.textAndMnemonic=Abrir
# File Size Units
FileChooser.fileSizeKiloBytes={0} KB
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_fr.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_fr.properties Thu Jun 04 18:49:37 2015 -0700
@@ -42,13 +42,13 @@
FileChooser.renameErrorFileExists.textAndMnemonic=Impossible de renommer {0} : il existe d\u00E9j\u00E0 un fichier portant le nom indiqu\u00E9. Indiquez-en un autre.
FileChooser.acceptAllFileFilter.textAndMnemonic=Tous les fichiers
FileChooser.cancelButton.textAndMnemonic=Annuler
-FileChooser.saveButton.textAndMnemonic=Enregi&strer
-FileChooser.openButton.textAndMnemonic=&Ouvrir
+FileChooser.saveButton.textAndMnemonic=Enregistrer
+FileChooser.openButton.textAndMnemonic=Ouvrir
FileChooser.saveDialogTitle.textAndMnemonic=Enregistrer
FileChooser.openDialogTitle.textAndMnemonic=Ouvrir
FileChooser.updateButton.textAndMnemonic=Mettre \u00E0 jo&ur
FileChooser.helpButton.textAndMnemonic=&Aide
-FileChooser.directoryOpenButton.textAndMnemonic=&Ouvrir
+FileChooser.directoryOpenButton.textAndMnemonic=Ouvrir
# File Size Units
FileChooser.fileSizeKiloBytes={0} KB
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_it.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_it.properties Thu Jun 04 18:49:37 2015 -0700
@@ -42,13 +42,13 @@
FileChooser.renameErrorFileExists.textAndMnemonic=Impossibile rinominare {0}: esiste gi\u00E0 un file con il nome specificato. Specificare un altro nome.
FileChooser.acceptAllFileFilter.textAndMnemonic=Tutti i file
FileChooser.cancelButton.textAndMnemonic=Annulla
-FileChooser.saveButton.textAndMnemonic=Sal&va
-FileChooser.openButton.textAndMnemonic=&Apri
+FileChooser.saveButton.textAndMnemonic=Salva
+FileChooser.openButton.textAndMnemonic=Apri
FileChooser.saveDialogTitle.textAndMnemonic=Salva
FileChooser.openDialogTitle.textAndMnemonic=Apri
FileChooser.updateButton.textAndMnemonic=Ag&giorna
FileChooser.helpButton.textAndMnemonic=&?
-FileChooser.directoryOpenButton.textAndMnemonic=&Apri
+FileChooser.directoryOpenButton.textAndMnemonic=Apri
# File Size Units
FileChooser.fileSizeKiloBytes={0} KB
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_pt_BR.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_pt_BR.properties Thu Jun 04 18:49:37 2015 -0700
@@ -42,13 +42,13 @@
FileChooser.renameErrorFileExists.textAndMnemonic=N\u00E3o \u00E9 poss\u00EDvel renomear {0}: Um arquivo com o nome especificado j\u00E1 existe. Especifique outro nome de arquivo.
FileChooser.acceptAllFileFilter.textAndMnemonic=Todos os Arquivos
FileChooser.cancelButton.textAndMnemonic=Cancelar
-FileChooser.saveButton.textAndMnemonic=&Salvar
-FileChooser.openButton.textAndMnemonic=A&brir
+FileChooser.saveButton.textAndMnemonic=Salvar
+FileChooser.openButton.textAndMnemonic=Abrir
FileChooser.saveDialogTitle.textAndMnemonic=Salvar
FileChooser.openDialogTitle.textAndMnemonic=Abrir
FileChooser.updateButton.textAndMnemonic=At&ualizar
FileChooser.helpButton.textAndMnemonic=Aj&uda
-FileChooser.directoryOpenButton.textAndMnemonic=A&brir
+FileChooser.directoryOpenButton.textAndMnemonic=Abrir
# File Size Units
FileChooser.fileSizeKiloBytes={0} KB
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_sv.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_sv.properties Thu Jun 04 18:49:37 2015 -0700
@@ -42,13 +42,13 @@
FileChooser.renameErrorFileExists.textAndMnemonic=Kan inte namn\u00E4ndra {0}: En fil med angivet namn finns redan. Ange ett annat filnamn.
FileChooser.acceptAllFileFilter.textAndMnemonic=Alla filer
FileChooser.cancelButton.textAndMnemonic=Avbryt
-FileChooser.saveButton.textAndMnemonic=&Spara
-FileChooser.openButton.textAndMnemonic=&\u00D6ppna
+FileChooser.saveButton.textAndMnemonic=Spara
+FileChooser.openButton.textAndMnemonic=\u00D6ppna
FileChooser.saveDialogTitle.textAndMnemonic=Spara
FileChooser.openDialogTitle.textAndMnemonic=\u00D6ppna
FileChooser.updateButton.textAndMnemonic=Upp&datera
FileChooser.helpButton.textAndMnemonic=&Hj\u00E4lp
-FileChooser.directoryOpenButton.textAndMnemonic=&\u00D6ppna
+FileChooser.directoryOpenButton.textAndMnemonic=\u00D6ppna
# File Size Units
FileChooser.fileSizeKiloBytes={0} KB
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal.properties Thu Jun 04 18:49:37 2015 -0700
@@ -43,8 +43,6 @@
FileChooser.fileTypeHeader.textAndMnemonic=Type
FileChooser.fileDateHeader.textAndMnemonic=Modified
FileChooser.fileAttrHeader.textAndMnemonic=Attributes
-FileChooser.saveButton.textAndMnemonic=Save
-FileChooser.openButton.textAndMnemonic=Open
############ Used by MetalTitlePane if rendering window decorations############
MetalTitlePane.restore.titleAndMnemonic=&Restore
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_de.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_de.properties Thu Jun 04 18:49:37 2015 -0700
@@ -43,8 +43,6 @@
FileChooser.fileTypeHeader.textAndMnemonic=Typ
FileChooser.fileDateHeader.textAndMnemonic=Ge\u00E4ndert
FileChooser.fileAttrHeader.textAndMnemonic=Attribute
-FileChooser.saveButton.textAndMnemonic=Speichern
-FileChooser.openButton.textAndMnemonic=\u00D6ffnen
############ Used by MetalTitlePane if rendering window decorations############
MetalTitlePane.restore.titleAndMnemonic=&Wiederherstellen
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_es.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_es.properties Thu Jun 04 18:49:37 2015 -0700
@@ -43,8 +43,6 @@
FileChooser.fileTypeHeader.textAndMnemonic=Tipo
FileChooser.fileDateHeader.textAndMnemonic=Modificado
FileChooser.fileAttrHeader.textAndMnemonic=Atributos
-FileChooser.saveButton.textAndMnemonic=Guardar
-FileChooser.openButton.textAndMnemonic=Abrir
############ Used by MetalTitlePane if rendering window decorations############
MetalTitlePane.restore.titleAndMnemonic=&Restaurar
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_fr.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_fr.properties Thu Jun 04 18:49:37 2015 -0700
@@ -43,8 +43,6 @@
FileChooser.fileTypeHeader.textAndMnemonic=Type
FileChooser.fileDateHeader.textAndMnemonic=Modifi\u00E9
FileChooser.fileAttrHeader.textAndMnemonic=Attributs
-FileChooser.saveButton.textAndMnemonic=Enregistrer
-FileChooser.openButton.textAndMnemonic=Ouvrir
############ Used by MetalTitlePane if rendering window decorations############
MetalTitlePane.restore.titleAndMnemonic=&Restaurer
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_it.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_it.properties Thu Jun 04 18:49:37 2015 -0700
@@ -43,8 +43,6 @@
FileChooser.fileTypeHeader.textAndMnemonic=Tipo
FileChooser.fileDateHeader.textAndMnemonic=Modificato
FileChooser.fileAttrHeader.textAndMnemonic=Attributi
-FileChooser.saveButton.textAndMnemonic=Salva
-FileChooser.openButton.textAndMnemonic=Apri
############ Used by MetalTitlePane if rendering window decorations############
MetalTitlePane.restore.titleAndMnemonic=&Ripristina
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_ja.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_ja.properties Thu Jun 04 18:49:37 2015 -0700
@@ -43,8 +43,6 @@
FileChooser.fileTypeHeader.textAndMnemonic=\u30BF\u30A4\u30D7
FileChooser.fileDateHeader.textAndMnemonic=\u4FEE\u6B63\u65E5
FileChooser.fileAttrHeader.textAndMnemonic=\u5C5E\u6027
-FileChooser.saveButton.textAndMnemonic=\u4FDD\u5B58
-FileChooser.openButton.textAndMnemonic=\u958B\u304F
############ Used by MetalTitlePane if rendering window decorations############
MetalTitlePane.restore.titleAndMnemonic=\u5FA9\u5143(&R)
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_ko.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_ko.properties Thu Jun 04 18:49:37 2015 -0700
@@ -43,8 +43,6 @@
FileChooser.fileTypeHeader.textAndMnemonic=\uC720\uD615
FileChooser.fileDateHeader.textAndMnemonic=\uC218\uC815 \uB0A0\uC9DC
FileChooser.fileAttrHeader.textAndMnemonic=\uC18D\uC131
-FileChooser.saveButton.textAndMnemonic=\uC800\uC7A5
-FileChooser.openButton.textAndMnemonic=\uC5F4\uAE30
############ Used by MetalTitlePane if rendering window decorations############
MetalTitlePane.restore.titleAndMnemonic=\uBCF5\uC6D0(&R)
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_pt_BR.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_pt_BR.properties Thu Jun 04 18:49:37 2015 -0700
@@ -43,8 +43,6 @@
FileChooser.fileTypeHeader.textAndMnemonic=Tipo
FileChooser.fileDateHeader.textAndMnemonic=Modificado
FileChooser.fileAttrHeader.textAndMnemonic=Atributos
-FileChooser.saveButton.textAndMnemonic=Salvar
-FileChooser.openButton.textAndMnemonic=Abrir
############ Used by MetalTitlePane if rendering window decorations############
MetalTitlePane.restore.titleAndMnemonic=&Restaurar
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_sv.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_sv.properties Thu Jun 04 18:49:37 2015 -0700
@@ -43,8 +43,6 @@
FileChooser.fileTypeHeader.textAndMnemonic=Typ
FileChooser.fileDateHeader.textAndMnemonic=\u00C4ndrad
FileChooser.fileAttrHeader.textAndMnemonic=Attribut
-FileChooser.saveButton.textAndMnemonic=Spara
-FileChooser.openButton.textAndMnemonic=\u00D6ppna
############ Used by MetalTitlePane if rendering window decorations############
MetalTitlePane.restore.titleAndMnemonic=&\u00C5terst\u00E4ll
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_zh_CN.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_zh_CN.properties Thu Jun 04 18:49:37 2015 -0700
@@ -43,8 +43,6 @@
FileChooser.fileTypeHeader.textAndMnemonic=\u7C7B\u578B
FileChooser.fileDateHeader.textAndMnemonic=\u4FEE\u6539\u65E5\u671F
FileChooser.fileAttrHeader.textAndMnemonic=\u5C5E\u6027
-FileChooser.saveButton.textAndMnemonic=\u4FDD\u5B58
-FileChooser.openButton.textAndMnemonic=\u6253\u5F00
############ Used by MetalTitlePane if rendering window decorations############
MetalTitlePane.restore.titleAndMnemonic=\u8FD8\u539F(&R)
--- a/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_zh_TW.properties Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources/metal_zh_TW.properties Thu Jun 04 18:49:37 2015 -0700
@@ -43,8 +43,6 @@
FileChooser.fileTypeHeader.textAndMnemonic=\u985E\u578B
FileChooser.fileDateHeader.textAndMnemonic=\u4FEE\u6539\u65E5\u671F
FileChooser.fileAttrHeader.textAndMnemonic=\u5C6C\u6027
-FileChooser.saveButton.textAndMnemonic=\u5132\u5B58
-FileChooser.openButton.textAndMnemonic=\u958B\u555F
############ Used by MetalTitlePane if rendering window decorations############
MetalTitlePane.restore.titleAndMnemonic=\u56DE\u5FA9(&R)
--- a/jdk/src/java.desktop/share/classes/java/awt/Component.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/java/awt/Component.java Thu Jun 04 18:49:37 2015 -0700
@@ -1312,6 +1312,25 @@
}
/**
+ * Determines the bounds of a visible part of the component relative to its
+ * parent.
+ *
+ * @return the visible part of bounds
+ */
+ private Rectangle getRecursivelyVisibleBounds() {
+ final Component container = getContainer();
+ final Rectangle bounds = getBounds();
+ if (container == null) {
+ // we are top level window or haven't a container, return our bounds
+ return bounds;
+ }
+ // translate the container's bounds to our coordinate space
+ final Rectangle parentsBounds = container.getRecursivelyVisibleBounds();
+ parentsBounds.setLocation(0, 0);
+ return parentsBounds.intersection(bounds);
+ }
+
+ /**
* Translates absolute coordinates into coordinates in the coordinate
* space of this component.
*/
@@ -1487,7 +1506,7 @@
ComponentPeer peer = this.peer;
if (peer != null) {
peer.setEnabled(true);
- if (visible) {
+ if (visible && !getRecursivelyVisibleBounds().isEmpty()) {
updateCursorImmediately();
}
}
@@ -1541,7 +1560,7 @@
ComponentPeer peer = this.peer;
if (peer != null) {
peer.setEnabled(false);
- if (visible) {
+ if (visible && !getRecursivelyVisibleBounds().isEmpty()) {
updateCursorImmediately();
}
}
--- a/jdk/src/java.desktop/share/classes/java/awt/Container.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/java/awt/Container.java Thu Jun 04 18:49:37 2015 -0700
@@ -44,6 +44,7 @@
import java.lang.ref.WeakReference;
import java.security.AccessController;
+import java.util.ArrayList;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Set;
@@ -100,7 +101,7 @@
* @see #add
* @see #getComponents
*/
- private java.util.List<Component> component = new java.util.ArrayList<Component>();
+ private java.util.List<Component> component = new ArrayList<>();
/**
* Layout manager for this container.
@@ -2568,28 +2569,24 @@
if (!contains(x, y)) {
return null;
}
+ Component lightweight = null;
synchronized (getTreeLock()) {
- // Two passes: see comment in sun.awt.SunGraphicsCallback
- for (int i = 0; i < component.size(); i++) {
- Component comp = component.get(i);
- if (comp != null &&
- !(comp.peer instanceof LightweightPeer)) {
- if (comp.contains(x - comp.x, y - comp.y)) {
+ // Optimized version of two passes:
+ // see comment in sun.awt.SunGraphicsCallback
+ for (final Component comp : component) {
+ if (comp.contains(x - comp.x, y - comp.y)) {
+ if (!comp.isLightweight()) {
+ // return heavyweight component as soon as possible
return comp;
}
- }
- }
- for (int i = 0; i < component.size(); i++) {
- Component comp = component.get(i);
- if (comp != null &&
- comp.peer instanceof LightweightPeer) {
- if (comp.contains(x - comp.x, y - comp.y)) {
- return comp;
+ if (lightweight == null) {
+ // save and return later the first lightweight component
+ lightweight = comp;
}
}
}
}
- return this;
+ return lightweight != null ? lightweight : this;
}
/**
@@ -2693,52 +2690,54 @@
return null;
}
- final Component findComponentAtImpl(int x, int y, boolean ignoreEnabled){
- checkTreeLock();
+ final Component findComponentAtImpl(int x, int y, boolean ignoreEnabled) {
+ // checkTreeLock(); commented for a performance reason
if (!(contains(x, y) && visible && (ignoreEnabled || enabled))) {
return null;
}
-
- // Two passes: see comment in sun.awt.SunGraphicsCallback
- for (int i = 0; i < component.size(); i++) {
- Component comp = component.get(i);
- if (comp != null &&
- !(comp.peer instanceof LightweightPeer)) {
- if (comp instanceof Container) {
- comp = ((Container)comp).findComponentAtImpl(x - comp.x,
- y - comp.y,
- ignoreEnabled);
- } else {
- comp = comp.getComponentAt(x - comp.x, y - comp.y);
+ Component lightweight = null;
+ // Optimized version of two passes:
+ // see comment in sun.awt.SunGraphicsCallback
+ for (final Component comp : component) {
+ final int x1 = x - comp.x;
+ final int y1 = y - comp.y;
+ if (!comp.contains(x1, y1)) {
+ continue; // fast path
+ }
+ if (!comp.isLightweight()) {
+ final Component child = getChildAt(comp, x1, y1, ignoreEnabled);
+ if (child != null) {
+ // return heavyweight component as soon as possible
+ return child;
}
- if (comp != null && comp.visible &&
- (ignoreEnabled || comp.enabled))
- {
- return comp;
+ } else {
+ if (lightweight == null) {
+ // save and return later the first lightweight component
+ lightweight = getChildAt(comp, x1, y1, ignoreEnabled);
}
}
}
- for (int i = 0; i < component.size(); i++) {
- Component comp = component.get(i);
- if (comp != null &&
- comp.peer instanceof LightweightPeer) {
- if (comp instanceof Container) {
- comp = ((Container)comp).findComponentAtImpl(x - comp.x,
- y - comp.y,
- ignoreEnabled);
- } else {
- comp = comp.getComponentAt(x - comp.x, y - comp.y);
- }
- if (comp != null && comp.visible &&
- (ignoreEnabled || comp.enabled))
- {
- return comp;
- }
- }
+ return lightweight != null ? lightweight : this;
+ }
+
+ /**
+ * Helper method for findComponentAtImpl. Finds a child component using
+ * findComponentAtImpl for Container and getComponentAt for Component.
+ */
+ private static Component getChildAt(Component comp, int x, int y,
+ boolean ignoreEnabled) {
+ if (comp instanceof Container) {
+ comp = ((Container) comp).findComponentAtImpl(x, y,
+ ignoreEnabled);
+ } else {
+ comp = comp.getComponentAt(x, y);
}
-
- return this;
+ if (comp != null && comp.visible &&
+ (ignoreEnabled || comp.enabled)) {
+ return comp;
+ }
+ return null;
}
/**
@@ -4420,6 +4419,18 @@
private static final PlatformLogger eventLog = PlatformLogger.getLogger("java.awt.event.LightweightDispatcher");
+ private static final int BUTTONS_DOWN_MASK;
+
+ static {
+ int[] buttonsDownMask = AWTAccessor.getInputEventAccessor().
+ getButtonDownMasks();
+ int mask = 0;
+ for (int buttonDownMask : buttonsDownMask) {
+ mask |= buttonDownMask;
+ }
+ BUTTONS_DOWN_MASK = mask;
+ }
+
LightweightDispatcher(Container nativeContainer) {
this.nativeContainer = nativeContainer;
mouseEventTarget = new WeakReference<>(null);
@@ -4488,25 +4499,12 @@
private boolean isMouseGrab(MouseEvent e) {
int modifiers = e.getModifiersEx();
- if(e.getID() == MouseEvent.MOUSE_PRESSED
- || e.getID() == MouseEvent.MOUSE_RELEASED)
- {
- switch (e.getButton()) {
- case MouseEvent.BUTTON1:
- modifiers ^= InputEvent.BUTTON1_DOWN_MASK;
- break;
- case MouseEvent.BUTTON2:
- modifiers ^= InputEvent.BUTTON2_DOWN_MASK;
- break;
- case MouseEvent.BUTTON3:
- modifiers ^= InputEvent.BUTTON3_DOWN_MASK;
- break;
- }
+ if (e.getID() == MouseEvent.MOUSE_PRESSED
+ || e.getID() == MouseEvent.MOUSE_RELEASED) {
+ modifiers ^= InputEvent.getMaskForButton(e.getButton());
}
/* modifiers now as just before event */
- return ((modifiers & (InputEvent.BUTTON1_DOWN_MASK
- | InputEvent.BUTTON2_DOWN_MASK
- | InputEvent.BUTTON3_DOWN_MASK)) != 0);
+ return ((modifiers & BUTTONS_DOWN_MASK) != 0);
}
/**
--- a/jdk/src/java.desktop/share/classes/java/awt/Menu.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/java/awt/Menu.java Thu Jun 04 18:49:37 2015 -0700
@@ -413,9 +413,9 @@
items.removeElementAt(index);
MenuPeer peer = (MenuPeer)this.peer;
if (peer != null) {
+ peer.delItem(index);
mi.removeNotify();
mi.parent = null;
- peer.delItem(index);
}
}
}
--- a/jdk/src/java.desktop/share/classes/java/awt/MenuBar.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/java/awt/MenuBar.java Thu Jun 04 18:49:37 2015 -0700
@@ -222,7 +222,6 @@
if (m.parent != null) {
m.parent.remove(m);
}
- menus.addElement(m);
m.parent = this;
MenuBarPeer peer = (MenuBarPeer)this.peer;
@@ -232,6 +231,7 @@
}
peer.addMenu(m);
}
+ menus.addElement(m);
return m;
}
}
@@ -248,9 +248,9 @@
menus.removeElementAt(index);
MenuBarPeer peer = (MenuBarPeer)this.peer;
if (peer != null) {
+ peer.delMenu(index);
m.removeNotify();
m.parent = null;
- peer.delMenu(index);
}
if (helpMenu == m) {
helpMenu = null;
--- a/jdk/src/java.desktop/share/classes/java/awt/MenuComponent.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/java/awt/MenuComponent.java Thu Jun 04 18:49:37 2015 -0700
@@ -77,7 +77,7 @@
* @see #setFont(Font)
* @see #getFont()
*/
- Font font;
+ volatile Font font;
/**
* The menu component's name, which defaults to <code>null</code>.
@@ -302,11 +302,13 @@
* @see java.awt.font.TextAttribute
*/
public void setFont(Font f) {
- font = f;
- //Fixed 6312943: NullPointerException in method MenuComponent.setFont(Font)
- MenuComponentPeer peer = this.peer;
- if (peer != null) {
- peer.setFont(f);
+ synchronized (getTreeLock()) {
+ font = f;
+ //Fixed 6312943: NullPointerException in method MenuComponent.setFont(Font)
+ MenuComponentPeer peer = this.peer;
+ if (peer != null) {
+ peer.setFont(f);
+ }
}
}
--- a/jdk/src/java.desktop/share/classes/java/awt/WaitDispatchSupport.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/java/awt/WaitDispatchSupport.java Thu Jun 04 18:49:37 2015 -0700
@@ -65,6 +65,7 @@
private AtomicBoolean keepBlockingEDT = new AtomicBoolean(false);
private AtomicBoolean keepBlockingCT = new AtomicBoolean(false);
+ private AtomicBoolean afterExit = new AtomicBoolean(false);
private static synchronized void initializeTimer() {
if (timer == null) {
@@ -114,7 +115,7 @@
}
boolean extEvaluate =
(extCondition != null) ? extCondition.evaluate() : true;
- if (!keepBlockingEDT.get() || !extEvaluate) {
+ if (!keepBlockingEDT.get() || !extEvaluate || afterExit.get()) {
if (timerTask != null) {
timerTask.cancel();
timerTask = null;
@@ -174,110 +175,116 @@
log.fine("The secondary loop is already running, aborting");
return false;
}
+ try {
+ if (afterExit.get()) {
+ log.fine("Exit was called already, aborting");
+ return false;
+ }
- final Runnable run = new Runnable() {
- public void run() {
- log.fine("Starting a new event pump");
- if (filter == null) {
- dispatchThread.pumpEvents(condition);
- } else {
- dispatchThread.pumpEventsForFilter(condition, filter);
+ final Runnable run = new Runnable() {
+ public void run() {
+ log.fine("Starting a new event pump");
+ if (filter == null) {
+ dispatchThread.pumpEvents(condition);
+ } else {
+ dispatchThread.pumpEventsForFilter(condition, filter);
+ }
}
- }
- };
+ };
+
+ // We have two mechanisms for blocking: if we're on the
+ // dispatch thread, start a new event pump; if we're
+ // on any other thread, call wait() on the treelock
- // We have two mechanisms for blocking: if we're on the
- // dispatch thread, start a new event pump; if we're
- // on any other thread, call wait() on the treelock
-
- Thread currentThread = Thread.currentThread();
- if (currentThread == dispatchThread) {
- if (log.isLoggable(PlatformLogger.Level.FINEST)) {
- log.finest("On dispatch thread: " + dispatchThread);
- }
- if (interval != 0) {
+ Thread currentThread = Thread.currentThread();
+ if (currentThread == dispatchThread) {
if (log.isLoggable(PlatformLogger.Level.FINEST)) {
- log.finest("scheduling the timer for " + interval + " ms");
+ log.finest("On dispatch thread: " + dispatchThread);
+ }
+ if (interval != 0) {
+ if (log.isLoggable(PlatformLogger.Level.FINEST)) {
+ log.finest("scheduling the timer for " + interval + " ms");
+ }
+ timer.schedule(timerTask = new TimerTask() {
+ @Override
+ public void run() {
+ if (keepBlockingEDT.compareAndSet(true, false)) {
+ wakeupEDT();
+ }
+ }
+ }, interval);
+ }
+ // Dispose SequencedEvent we are dispatching on the current
+ // AppContext, to prevent us from hang - see 4531693 for details
+ SequencedEvent currentSE = KeyboardFocusManager.
+ getCurrentKeyboardFocusManager().getCurrentSequencedEvent();
+ if (currentSE != null) {
+ if (log.isLoggable(PlatformLogger.Level.FINE)) {
+ log.fine("Dispose current SequencedEvent: " + currentSE);
+ }
+ currentSE.dispose();
}
- timer.schedule(timerTask = new TimerTask() {
- @Override
- public void run() {
- if (keepBlockingEDT.compareAndSet(true, false)) {
- wakeupEDT();
+ // In case the exit() method is called before starting
+ // new event pump it will post the waking event to EDT.
+ // The event will be handled after the new event pump
+ // starts. Thus, the enter() method will not hang.
+ //
+ // Event pump should be privileged. See 6300270.
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ run.run();
+ return null;
+ }
+ });
+ } else {
+ if (log.isLoggable(PlatformLogger.Level.FINEST)) {
+ log.finest("On non-dispatch thread: " + currentThread);
+ }
+ keepBlockingCT.set(true);
+ synchronized (getTreeLock()) {
+ if (afterExit.get()) return false;
+ if (filter != null) {
+ dispatchThread.addEventFilter(filter);
+ }
+ try {
+ EventQueue eq = dispatchThread.getEventQueue();
+ eq.postEvent(new PeerEvent(this, run, PeerEvent.PRIORITY_EVENT));
+ if (interval > 0) {
+ long currTime = System.currentTimeMillis();
+ while (keepBlockingCT.get() &&
+ ((extCondition != null) ? extCondition.evaluate() : true) &&
+ (currTime + interval > System.currentTimeMillis()))
+ {
+ getTreeLock().wait(interval);
+ }
+ } else {
+ while (keepBlockingCT.get() &&
+ ((extCondition != null) ? extCondition.evaluate() : true))
+ {
+ getTreeLock().wait();
+ }
+ }
+ if (log.isLoggable(PlatformLogger.Level.FINE)) {
+ log.fine("waitDone " + keepBlockingEDT.get() + " " + keepBlockingCT.get());
+ }
+ } catch (InterruptedException e) {
+ if (log.isLoggable(PlatformLogger.Level.FINE)) {
+ log.fine("Exception caught while waiting: " + e);
+ }
+ } finally {
+ if (filter != null) {
+ dispatchThread.removeEventFilter(filter);
}
}
- }, interval);
- }
- // Dispose SequencedEvent we are dispatching on the current
- // AppContext, to prevent us from hang - see 4531693 for details
- SequencedEvent currentSE = KeyboardFocusManager.
- getCurrentKeyboardFocusManager().getCurrentSequencedEvent();
- if (currentSE != null) {
- if (log.isLoggable(PlatformLogger.Level.FINE)) {
- log.fine("Dispose current SequencedEvent: " + currentSE);
}
- currentSE.dispose();
- }
- // In case the exit() method is called before starting
- // new event pump it will post the waking event to EDT.
- // The event will be handled after the new event pump
- // starts. Thus, the enter() method will not hang.
- //
- // Event pump should be privileged. See 6300270.
- AccessController.doPrivileged(new PrivilegedAction<Void>() {
- public Void run() {
- run.run();
- return null;
- }
- });
- } else {
- if (log.isLoggable(PlatformLogger.Level.FINEST)) {
- log.finest("On non-dispatch thread: " + currentThread);
}
- synchronized (getTreeLock()) {
- if (filter != null) {
- dispatchThread.addEventFilter(filter);
- }
- try {
- EventQueue eq = dispatchThread.getEventQueue();
- eq.postEvent(new PeerEvent(this, run, PeerEvent.PRIORITY_EVENT));
- keepBlockingCT.set(true);
- if (interval > 0) {
- long currTime = System.currentTimeMillis();
- while (keepBlockingCT.get() &&
- ((extCondition != null) ? extCondition.evaluate() : true) &&
- (currTime + interval > System.currentTimeMillis()))
- {
- getTreeLock().wait(interval);
- }
- } else {
- while (keepBlockingCT.get() &&
- ((extCondition != null) ? extCondition.evaluate() : true))
- {
- getTreeLock().wait();
- }
- }
- if (log.isLoggable(PlatformLogger.Level.FINE)) {
- log.fine("waitDone " + keepBlockingEDT.get() + " " + keepBlockingCT.get());
- }
- } catch (InterruptedException e) {
- if (log.isLoggable(PlatformLogger.Level.FINE)) {
- log.fine("Exception caught while waiting: " + e);
- }
- } finally {
- if (filter != null) {
- dispatchThread.removeEventFilter(filter);
- }
- }
- // If the waiting process has been stopped because of the
- // time interval passed or an exception occurred, the state
- // should be changed
- keepBlockingEDT.set(false);
- keepBlockingCT.set(false);
- }
+ return true;
}
-
- return true;
+ finally {
+ keepBlockingEDT.set(false);
+ keepBlockingCT.set(false);
+ afterExit.set(false);
+ }
}
/**
@@ -288,7 +295,8 @@
log.fine("exit(): blockingEDT=" + keepBlockingEDT.get() +
", blockingCT=" + keepBlockingCT.get());
}
- if (keepBlockingEDT.compareAndSet(true, false)) {
+ afterExit.set(true);
+ if (keepBlockingEDT.getAndSet(false)) {
wakeupEDT();
return true;
}
--- a/jdk/src/java.desktop/share/classes/java/awt/font/OpenType.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/java/awt/font/OpenType.java Thu Jun 04 18:49:37 2015 -0700
@@ -331,7 +331,7 @@
* Optical bounds. Table tag "opbd" in the Open
* Type Specification.
*/
- public final static int TAG_OPBD = 0x6d6f7274;
+ public final static int TAG_OPBD = 0x6F706264;
/**
* Glyph properties. Table tag "prop" in the Open
--- a/jdk/src/java.desktop/share/classes/java/beans/Introspector.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/java/beans/Introspector.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -35,16 +35,19 @@
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.EventObject;
import java.util.List;
import java.util.TreeMap;
+import sun.misc.JavaBeansAccess;
import sun.misc.SharedSecrets;
import sun.reflect.misc.ReflectUtil;
@@ -146,15 +149,25 @@
// register with SharedSecrets for JMX usage
static {
- SharedSecrets.setJavaBeansIntrospectorAccess((clazz, property) -> {
- BeanInfo bi = Introspector.getBeanInfo(clazz);
- PropertyDescriptor[] pds = bi.getPropertyDescriptors();
- for (PropertyDescriptor pd: pds) {
- if (pd.getName().equals(property)) {
- return pd.getReadMethod();
+ SharedSecrets.setJavaBeansAccess(new JavaBeansAccess() {
+ @Override
+ public Method getReadMethod(Class<?> clazz, String property) throws Exception {
+ BeanInfo bi = Introspector.getBeanInfo(clazz);
+ PropertyDescriptor[] pds = bi.getPropertyDescriptors();
+ for (PropertyDescriptor pd: pds) {
+ if (pd.getName().equals(property)) {
+ return pd.getReadMethod();
+ }
}
+ return null;
}
- return null;
+
+ @Override
+ public String[] getConstructorPropertiesValue(Constructor<?> ctr) {
+ ConstructorProperties cp = ctr.getAnnotation(ConstructorProperties.class);
+ String [] ret = cp != null ? cp.value() : null;
+ return ret;
+ }
});
}
--- a/jdk/src/java.desktop/share/classes/javax/sound/sampled/spi/AudioFileWriter.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/sound/sampled/spi/AudioFileWriter.java Thu Jun 04 18:49:37 2015 -0700
@@ -121,7 +121,7 @@
* @throws IOException if an I/O exception occurs
* @throws IllegalArgumentException if the file type is not supported by the
* system
- * @see #isFileTypeSupported(Type, AudioInputStream)
+ * @see #isFileTypeSupported(AudioFileFormat.Type, AudioInputStream)
* @see #getAudioFileTypes
*/
public abstract int write(AudioInputStream stream, Type fileType,
--- a/jdk/src/java.desktop/share/classes/javax/swing/GroupLayout.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/GroupLayout.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -218,6 +218,9 @@
private static final int UNSET = Integer.MIN_VALUE;
+ // Maximum spring size constrain to avoid integer overflow
+ private static final int INFINITE = Integer.MAX_VALUE >> 1;
+
/**
* Indicates the size from the component or gap should be used for a
* particular range value.
@@ -1389,7 +1392,7 @@
}
int constrain(int value) {
- return Math.min(value, Short.MAX_VALUE);
+ return Math.min(value, INFINITE);
}
int getBaseline() {
--- a/jdk/src/java.desktop/share/classes/javax/swing/JApplet.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/JApplet.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,14 +24,18 @@
*/
package javax.swing;
-import java.awt.*;
-import java.awt.event.*;
import java.applet.Applet;
-import java.beans.PropertyChangeListener;
-import java.util.Locale;
-import java.util.Vector;
-import java.io.Serializable;
-import javax.accessibility.*;
+import java.awt.AWTEvent;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Graphics;
+import java.awt.HeadlessException;
+import java.awt.LayoutManager;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
/**
* An extended version of <code>java.applet.Applet</code> that adds support for
@@ -243,9 +247,8 @@
* hidden: true
* description: The menubar for accessing pulldown menus from this applet.
*/
- @SuppressWarnings("deprecation")
- public void setJMenuBar(JMenuBar menuBar) {
- getRootPane().setMenuBar(menuBar);
+ public void setJMenuBar(final JMenuBar menuBar) {
+ getRootPane().setJMenuBar(menuBar);
}
/**
@@ -254,9 +257,8 @@
* @return the menubar set on this applet
* @see #setJMenuBar
*/
- @SuppressWarnings("deprecation")
public JMenuBar getJMenuBar() {
- return getRootPane().getMenuBar();
+ return getRootPane().getJMenuBar();
}
--- a/jdk/src/java.desktop/share/classes/javax/swing/JDialog.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/JDialog.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -849,9 +849,8 @@
* hidden: true
* description: The menubar for accessing pulldown menus from this dialog.
*/
- @SuppressWarnings("deprecation")
- public void setJMenuBar(JMenuBar menu) {
- getRootPane().setMenuBar(menu);
+ public void setJMenuBar(final JMenuBar menu) {
+ getRootPane().setJMenuBar(menu);
}
/**
@@ -860,12 +859,10 @@
* @return the menubar set on this dialog
* @see #setJMenuBar
*/
- @SuppressWarnings("deprecation")
public JMenuBar getJMenuBar() {
- return getRootPane().getMenuBar();
+ return getRootPane().getJMenuBar();
}
-
/**
* Returns whether calls to {@code add} and
* {@code setLayout} are forwarded to the {@code contentPane}.
--- a/jdk/src/java.desktop/share/classes/javax/swing/JFrame.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/JFrame.java Thu Jun 04 18:49:37 2015 -0700
@@ -486,9 +486,8 @@
* hidden: true
* description: The menubar for accessing pulldown menus from this frame.
*/
- @SuppressWarnings("deprecation")
- public void setJMenuBar(JMenuBar menubar) {
- getRootPane().setMenuBar(menubar);
+ public void setJMenuBar(final JMenuBar menubar) {
+ getRootPane().setJMenuBar(menubar);
}
/**
@@ -497,9 +496,8 @@
*
* @see #setJMenuBar
*/
- @SuppressWarnings("deprecation")
public JMenuBar getJMenuBar() {
- return getRootPane().getMenuBar();
+ return getRootPane().getJMenuBar();
}
/**
--- a/jdk/src/java.desktop/share/classes/javax/swing/JSpinner.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/JSpinner.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -42,8 +42,6 @@
import javax.accessibility.*;
import sun.util.locale.provider.LocaleProviderAdapter;
import sun.util.locale.provider.LocaleResources;
-import sun.util.locale.provider.LocaleServiceProviderPool;
-
/**
* A single line input field that lets the user select a
@@ -77,12 +75,12 @@
* try {
* spinner.commitEdit();
* }
- * catch (ParseException pe) {{
+ * catch (ParseException pe) {
* // Edited value is invalid, spinner.getValue() will return
* // the last valid value, you could revert the spinner to show that:
- * JComponent editor = spinner.getEditor()
+ * JComponent editor = spinner.getEditor();
* if (editor instanceof DefaultEditor) {
- * ((DefaultEditor)editor).getTextField().setValue(spinner.getValue();
+ * ((DefaultEditor)editor).getTextField().setValue(spinner.getValue());
* }
* // reset the value to some known value:
* spinner.setValue(fallbackValue);
@@ -972,7 +970,7 @@
* and editing the value of a <code>SpinnerDateModel</code>
* with a <code>JFormattedTextField</code>. <code>This</code>
* <code>DateEditor</code> becomes both a <code>ChangeListener</code>
- * on the spinners model and a <code>PropertyChangeListener</code>
+ * on the spinner and a <code>PropertyChangeListener</code>
* on the new <code>JFormattedTextField</code>.
*
* @param spinner the spinner whose model <code>this</code> editor will monitor
--- a/jdk/src/java.desktop/share/classes/javax/swing/RepaintManager.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/RepaintManager.java Thu Jun 04 18:49:37 2015 -0700
@@ -182,9 +182,16 @@
*/
private final ProcessingRunnable processingRunnable;
- private final static JavaSecurityAccess javaSecurityAccess =
- SharedSecrets.getJavaSecurityAccess();
+ private static final JavaSecurityAccess javaSecurityAccess =
+ SharedSecrets.getJavaSecurityAccess();
+ /**
+ * Listener installed to detect display changes. When display changes,
+ * schedules a callback to notify all RepaintManagers of the display
+ * changes.
+ */
+ private static final DisplayChangedListener displayChangedHandler =
+ new DisplayChangedHandler();
static {
SwingAccessor.setRepaintManagerAccessor(new SwingAccessor.RepaintManagerAccessor() {
@@ -226,8 +233,8 @@
GraphicsEnvironment ge = GraphicsEnvironment.
getLocalGraphicsEnvironment();
if (ge instanceof SunGraphicsEnvironment) {
- ((SunGraphicsEnvironment)ge).addDisplayChangedListener(
- new DisplayChangedHandler());
+ ((SunGraphicsEnvironment) ge).addDisplayChangedListener(
+ displayChangedHandler);
}
Toolkit tk = Toolkit.getDefaultToolkit();
if ((tk instanceof SunToolkit)
@@ -1679,6 +1686,12 @@
*/
private static final class DisplayChangedHandler implements
DisplayChangedListener {
+ // Empty non private constructor was added because access to this
+ // class shouldn't be generated by the compiler using synthetic
+ // accessor method
+ DisplayChangedHandler() {
+ }
+
public void displayChanged() {
scheduleDisplayChanges();
}
@@ -1686,11 +1699,10 @@
public void paletteChanged() {
}
- private void scheduleDisplayChanges() {
+ private static void scheduleDisplayChanges() {
// To avoid threading problems, we notify each RepaintManager
// on the thread it was created on.
- for (Object c : AppContext.getAppContexts()) {
- AppContext context = (AppContext) c;
+ for (AppContext context : AppContext.getAppContexts()) {
synchronized(context) {
if (!context.isDisposed()) {
EventQueue eventQueue = (EventQueue)context.get(
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java Thu Jun 04 18:49:37 2015 -0700
@@ -150,6 +150,8 @@
*/
protected KeyListener popupKeyListener;
+ private MouseWheelListener mouseWheelListener;
+
// This is used for knowing when to cache the minimum preferred size.
// If the data in the list changes, the cached value get marked for recalc.
// Added to the current JComboBox model
@@ -413,6 +415,10 @@
comboBox.getModel().addListDataListener( listDataListener );
}
}
+
+ if ((mouseWheelListener = createMouseWheelListener()) != null) {
+ comboBox.addMouseWheelListener(mouseWheelListener);
+ }
}
/**
@@ -459,6 +465,9 @@
comboBox.getModel().removeListDataListener( listDataListener );
}
}
+ if (mouseWheelListener != null) {
+ comboBox.removeMouseWheelListener(mouseWheelListener);
+ }
}
/**
@@ -572,6 +581,10 @@
return handler;
}
+ private MouseWheelListener createMouseWheelListener() {
+ return getHandler();
+ }
+
//
// end UI Initialization
//======================
@@ -1723,7 +1736,8 @@
//
private class Handler implements ActionListener, FocusListener,
KeyListener, LayoutManager,
- ListDataListener, PropertyChangeListener {
+ ListDataListener, PropertyChangeListener,
+ MouseWheelListener {
//
// PropertyChangeListener
//
@@ -1997,21 +2011,25 @@
public void actionPerformed(ActionEvent evt) {
Object item = comboBox.getEditor().getItem();
if (item != null) {
- if(!comboBox.isPopupVisible() && !item.equals(comboBox.getSelectedItem())) {
- comboBox.setSelectedItem(comboBox.getEditor().getItem());
- }
- ActionMap am = comboBox.getActionMap();
- if (am != null) {
- Action action = am.get("enterPressed");
- if (action != null) {
- action.actionPerformed(new ActionEvent(comboBox, evt.getID(),
- evt.getActionCommand(),
- evt.getModifiers()));
+ if (!comboBox.isPopupVisible() && !item.equals(comboBox.getSelectedItem())) {
+ comboBox.setSelectedItem(comboBox.getEditor().getItem());
+ }
+ ActionMap am = comboBox.getActionMap();
+ if (am != null) {
+ Action action = am.get("enterPressed");
+ if (action != null) {
+ action.actionPerformed(new ActionEvent(comboBox, evt.getID(),
+ evt.getActionCommand(),
+ evt.getModifiers()));
+ }
}
}
- }
+ }
+
+ public void mouseWheelMoved(MouseWheelEvent e) {
+ e.consume();
+ }
}
- }
class DefaultKeySelectionManager implements JComboBox.KeySelectionManager, UIResource {
private String prefix = "";
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -184,6 +184,8 @@
*/
protected ItemListener itemListener;
+ private MouseWheelListener scrollerMouseWheelListener;
+
/**
* This protected field is implementation specific. Do not access directly
* or override.
@@ -311,6 +313,7 @@
uninstallComboBoxModelListeners(comboBox.getModel());
uninstallKeyboardActions();
uninstallListListeners();
+ uninstallScrollerListeners();
// We do this, otherwise the listener the ui installs on
// the model (the combobox model in this case) will keep a
// reference to the list, causing the list (and us) to never get gced.
@@ -608,6 +611,7 @@
scroller.setFocusable( false );
scroller.getVerticalScrollBar().setFocusable( false );
scroller.setBorder( null );
+ installScrollerListeners();
}
/**
@@ -624,6 +628,20 @@
setFocusable( false );
}
+ private void installScrollerListeners() {
+ scrollerMouseWheelListener = getHandler();
+ if (scrollerMouseWheelListener != null) {
+ scroller.addMouseWheelListener(scrollerMouseWheelListener);
+ }
+ }
+
+ private void uninstallScrollerListeners() {
+ if (scrollerMouseWheelListener != null) {
+ scroller.removeMouseWheelListener(scrollerMouseWheelListener);
+ scrollerMouseWheelListener = null;
+ }
+ }
+
/**
* This method adds the necessary listeners to the JComboBox.
*/
@@ -835,8 +853,8 @@
private class Handler implements ItemListener, MouseListener,
- MouseMotionListener, PropertyChangeListener,
- Serializable {
+ MouseMotionListener, MouseWheelListener,
+ PropertyChangeListener, Serializable {
//
// MouseListener
// NOTE: this is added to both the JList and JComboBox
@@ -1024,6 +1042,13 @@
setListSelection(comboBox.getSelectedIndex());
}
}
+
+ //
+ // MouseWheelListener
+ //
+ public void mouseWheelMoved(MouseWheelEvent e) {
+ e.consume();
+ }
}
//
@@ -1287,11 +1312,24 @@
else {
screenBounds = new Rectangle(p, toolkit.getScreenSize());
}
-
- Rectangle rect = new Rectangle(px,py,pw,ph);
- if (py+ph > screenBounds.y+screenBounds.height
- && ph < screenBounds.height) {
- rect.y = -rect.height;
+ int borderHeight = 0;
+ Border popupBorder = getBorder();
+ if (popupBorder != null) {
+ Insets borderInsets = popupBorder.getBorderInsets(this);
+ borderHeight = borderInsets.top + borderInsets.bottom;
+ screenBounds.width -= (borderInsets.left + borderInsets.right);
+ screenBounds.height -= borderHeight;
+ }
+ Rectangle rect = new Rectangle(px, py, pw, ph);
+ if (py + ph > screenBounds.y + screenBounds.height) {
+ if (ph <= -screenBounds.y - borderHeight) {
+ // popup goes above
+ rect.y = -ph - borderHeight;
+ } else {
+ // a full screen height popup
+ rect.y = screenBounds.y + Math.max(0, (screenBounds.height - ph) / 2 );
+ rect.height = Math.min(screenBounds.height, ph);
+ }
}
return rect;
}
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -504,7 +504,7 @@
doPress(label);
}
else if (key == RELEASE) {
- doRelease(label);
+ doRelease(label, e.getActionCommand() != null);
}
}
@@ -517,33 +517,77 @@
SwingUtilities.replaceUIInputMap(label, JComponent.WHEN_FOCUSED, inputMap);
}
int dka = label.getDisplayedMnemonic();
- inputMap.put(KeyStroke.getKeyStroke(dka, BasicLookAndFeel.getFocusAcceleratorKeyMask(), true), RELEASE);
+ putOnRelease(inputMap, dka, BasicLookAndFeel
+ .getFocusAcceleratorKeyMask());
// Need this when the sticky keys are enabled
- inputMap.put(KeyStroke.getKeyStroke(dka, 0, true), RELEASE);
+ putOnRelease(inputMap, dka, 0);
// Need this if ALT is released before the accelerator
- inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ALT, 0, true), RELEASE);
+ putOnRelease(inputMap, KeyEvent.VK_ALT, 0);
label.requestFocus();
}
}
- private void doRelease(JLabel label) {
+ private void doRelease(JLabel label, boolean isCommand) {
Component labelFor = label.getLabelFor();
if (labelFor != null && labelFor.isEnabled()) {
- InputMap inputMap = SwingUtilities.getUIInputMap(label, JComponent.WHEN_FOCUSED);
- if (inputMap != null) {
- // inputMap should never be null.
+ if (label.hasFocus()) {
+ InputMap inputMap = SwingUtilities.getUIInputMap(label,
+ JComponent.WHEN_FOCUSED);
+ if (inputMap != null) {
+ // inputMap should never be null.
+ int dka = label.getDisplayedMnemonic();
+ removeOnRelease(inputMap, dka, BasicLookAndFeel
+ .getFocusAcceleratorKeyMask());
+ removeOnRelease(inputMap, dka, 0);
+ removeOnRelease(inputMap, KeyEvent.VK_ALT, 0);
+ }
+ inputMap = SwingUtilities.getUIInputMap(label,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ if (inputMap == null) {
+ inputMap = new InputMapUIResource();
+ SwingUtilities.replaceUIInputMap(label,
+ JComponent.WHEN_IN_FOCUSED_WINDOW, inputMap);
+ }
int dka = label.getDisplayedMnemonic();
- inputMap.remove(KeyStroke.getKeyStroke(dka, BasicLookAndFeel.getFocusAcceleratorKeyMask(), true));
- inputMap.remove(KeyStroke.getKeyStroke(dka, 0, true));
- inputMap.remove(KeyStroke.getKeyStroke(KeyEvent.VK_ALT, 0, true));
- }
- if (labelFor instanceof Container &&
- ((Container) labelFor).isFocusCycleRoot()) {
- labelFor.requestFocus();
+ if (isCommand) {
+ putOnRelease(inputMap, KeyEvent.VK_ALT, 0);
+ } else {
+ putOnRelease(inputMap, dka, BasicLookAndFeel
+ .getFocusAcceleratorKeyMask());
+ // Need this when the sticky keys are enabled
+ putOnRelease(inputMap, dka, 0);
+ }
+ if (labelFor instanceof Container &&
+ ((Container) labelFor).isFocusCycleRoot()) {
+ labelFor.requestFocus();
+ } else {
+ SwingUtilities2.compositeRequestFocus(labelFor);
+ }
} else {
- SwingUtilities2.compositeRequestFocus(labelFor);
+ InputMap inputMap = SwingUtilities.getUIInputMap(label,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ int dka = label.getDisplayedMnemonic();
+ if (inputMap != null) {
+ if (isCommand) {
+ removeOnRelease(inputMap, dka, BasicLookAndFeel
+ .getFocusAcceleratorKeyMask());
+ removeOnRelease(inputMap, dka, 0);
+ } else {
+ removeOnRelease(inputMap, KeyEvent.VK_ALT, 0);
+ }
+ }
}
}
}
+
+ private void putOnRelease(InputMap inputMap, int keyCode, int modifiers) {
+ inputMap.put(KeyStroke.getKeyStroke(keyCode, modifiers, true),
+ RELEASE);
+ }
+
+ private void removeOnRelease(InputMap inputMap, int keyCode, int modifiers) {
+ inputMap.remove(KeyStroke.getKeyStroke(keyCode, modifiers, true));
+ }
+
}
}
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java Thu Jun 04 18:49:37 2015 -0700
@@ -914,7 +914,9 @@
processMouseEvent(me);
break;
case MouseEvent.MOUSE_WHEEL:
- if (isInPopup(src)) {
+ if (isInPopup(src)
+ || ((src instanceof JComboBox) && ((JComboBox) src).isPopupVisible())) {
+
return;
}
cancelPopupMenu();
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRadioButtonUI.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRadioButtonUI.java Thu Jun 04 18:49:37 2015 -0700
@@ -438,7 +438,7 @@
// to the button group or not
Component getFocusTransferBaseComponent(boolean next){
Component focusBaseComp = activeBtn;
- Window container = SwingUtilities.getWindowAncestor(activeBtn);
+ Container container = focusBaseComp.getFocusCycleRootAncestor();
if (container != null) {
FocusTraversalPolicy policy = container.getFocusTraversalPolicy();
Component comp = next ? policy.getComponentAfter(container, activeBtn)
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -989,7 +989,7 @@
((JSpinner.DefaultEditor)newEditor).getTextField();
if (tf != null) {
if (tf.getFont() instanceof UIResource) {
- tf.setFont(spinner.getFont());
+ tf.setFont(new FontUIResource(spinner.getFont()));
}
tf.addFocusListener(nextButtonHandler);
tf.addFocusListener(previousButtonHandler);
@@ -1002,12 +1002,12 @@
}
else if ("font".equals(propertyName)) {
JComponent editor = spinner.getEditor();
- if (editor!=null && editor instanceof JSpinner.DefaultEditor) {
+ if (editor instanceof JSpinner.DefaultEditor) {
JTextField tf =
((JSpinner.DefaultEditor)editor).getTextField();
if (tf != null) {
if (tf.getFont() instanceof UIResource) {
- tf.setFont(spinner.getFont());
+ tf.setFont(new FontUIResource(spinner.getFont()));
}
}
}
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -936,10 +936,11 @@
((AbstractDocument)doc).readLock();
}
try {
- if ((d.width > (i.left + i.right)) && (d.height > (i.top + i.bottom))) {
- rootView.setSize(d.width - i.left - i.right, d.height - i.top - i.bottom);
+ if ((d.width > (i.left + i.right + caretMargin)) && (d.height > (i.top + i.bottom))) {
+ rootView.setSize(d.width - i.left - i.right -
+ caretMargin, d.height - i.top - i.bottom);
}
- else if (d.width == 0 && d.height == 0) {
+ else if (d.width == 0 || d.height == 0) {
// Probably haven't been layed out yet, force some sort of
// initial sizing.
rootView.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalRootPaneUI.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalRootPaneUI.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,15 +27,11 @@
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
import javax.swing.*;
-import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import java.awt.*;
-import java.io.*;
-import java.security.*;
/**
* Provides the metal look and feel implementation of <code>RootPaneUI</code>.
@@ -441,7 +437,6 @@
* @param the Container for which this layout manager is being used
* @return a Dimension object containing the layout's preferred size
*/
- @SuppressWarnings("deprecation")
public Dimension preferredLayoutSize(Container parent) {
Dimension cpd, mbd, tpd;
int cpWidth = 0;
@@ -463,8 +458,8 @@
cpHeight = cpd.height;
}
- if(root.getMenuBar() != null) {
- mbd = root.getMenuBar().getPreferredSize();
+ if(root.getJMenuBar() != null) {
+ mbd = root.getJMenuBar().getPreferredSize();
if (mbd != null) {
mbWidth = mbd.width;
mbHeight = mbd.height;
@@ -494,7 +489,6 @@
* @param the Container for which this layout manager is being used
* @return a Dimension object containing the layout's minimum size
*/
- @SuppressWarnings("deprecation")
public Dimension minimumLayoutSize(Container parent) {
Dimension cpd, mbd, tpd;
int cpWidth = 0;
@@ -516,8 +510,8 @@
cpHeight = cpd.height;
}
- if(root.getMenuBar() != null) {
- mbd = root.getMenuBar().getMinimumSize();
+ if(root.getJMenuBar() != null) {
+ mbd = root.getJMenuBar().getMinimumSize();
if (mbd != null) {
mbWidth = mbd.width;
mbHeight = mbd.height;
@@ -546,7 +540,6 @@
* @param the Container for which this layout manager is being used
* @return a Dimension object containing the layout's maximum size
*/
- @SuppressWarnings("deprecation")
public Dimension maximumLayoutSize(Container target) {
Dimension cpd, mbd, tpd;
int cpWidth = Integer.MAX_VALUE;
@@ -566,8 +559,8 @@
}
}
- if(root.getMenuBar() != null) {
- mbd = root.getMenuBar().getMaximumSize();
+ if(root.getJMenuBar() != null) {
+ mbd = root.getJMenuBar().getMaximumSize();
if (mbd != null) {
mbWidth = mbd.width;
mbHeight = mbd.height;
@@ -610,7 +603,6 @@
*
* @param the Container for which this layout manager is being used
*/
- @SuppressWarnings("deprecation")
public void layoutContainer(Container parent) {
JRootPane root = (JRootPane) parent;
Rectangle b = root.getBounds();
@@ -640,9 +632,9 @@
}
}
}
- if(root.getMenuBar() != null) {
- Dimension mbd = root.getMenuBar().getPreferredSize();
- root.getMenuBar().setBounds(0, nextY, w, mbd.height);
+ if(root.getJMenuBar() != null) {
+ Dimension mbd = root.getJMenuBar().getPreferredSize();
+ root.getJMenuBar().setBounds(0, nextY, w, mbd.height);
nextY += mbd.height;
}
if(root.getContentPane() != null) {
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthSplitPaneUI.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthSplitPaneUI.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -134,6 +134,7 @@
value = Integer.valueOf(6);
}
LookAndFeel.installProperty(splitPane, "dividerSize", value);
+ dividerSize = ((Number)value).intValue();
value = style.get(context, "SplitPane.oneTouchExpandable");
if (value != null) {
--- a/jdk/src/java.desktop/share/classes/sun/applet/AppletClassLoader.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/applet/AppletClassLoader.java Thu Jun 04 18:49:37 2015 -0700
@@ -802,7 +802,7 @@
/**
* Determine if applet is targeted for JDK 1.1.
*
- * @param applet Applet class.
+ * @param clazz Applet class.
* @return TRUE if applet is targeted for JDK 1.1;
* FALSE if applet is not;
* null if applet is unknown.
@@ -815,7 +815,7 @@
/**
* Determine if applet is targeted for JDK 1.2.
*
- * @param applet Applet class.
+ * @param clazz Applet class.
* @return TRUE if applet is targeted for JDK 1.2;
* FALSE if applet is not;
* null if applet is unknown.
--- a/jdk/src/java.desktop/share/classes/sun/applet/AppletSecurity.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/applet/AppletSecurity.java Thu Jun 04 18:49:37 2015 -0700
@@ -270,10 +270,10 @@
* The <code>checkPackageAccess</code> method for class
* <code>SecurityManager</code> calls
* <code>checkPermission</code> with the
- * <code>RuntimePermission("accessClassInPackage."+pkg)</code>
+ * <code>RuntimePermission("accessClassInPackage."+ pkgname)</code>
* permission.
*
- * @param pkg the package name.
+ * @param pkgname the package name.
* @exception SecurityException if the caller does not have
* permission to access the specified package.
* @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
--- a/jdk/src/java.desktop/share/classes/sun/awt/AppContext.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/AppContext.java Thu Jun 04 18:49:37 2015 -0700
@@ -190,7 +190,7 @@
*
* @see #addPropertyChangeListener
* @see #removePropertyChangeListener
- * @see #firePropertyChange
+ * @see PropertyChangeSupport#firePropertyChange
*/
private PropertyChangeSupport changeSupport = null;
@@ -809,7 +809,7 @@
*
* @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
* @see #getPropertyChangeListeners(java.lang.String)
- * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
+ * @see PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
*/
public synchronized void removePropertyChangeListener(
String propertyName,
--- a/jdk/src/java.desktop/share/classes/sun/awt/DefaultMouseInfoPeer.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/DefaultMouseInfoPeer.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,7 +29,7 @@
import java.awt.Window;
import java.awt.peer.MouseInfoPeer;
-public class DefaultMouseInfoPeer implements MouseInfoPeer {
+public final class DefaultMouseInfoPeer implements MouseInfoPeer {
/**
* Package-private constructor to prevent instantiation.
--- a/jdk/src/java.desktop/share/classes/sun/awt/EmbeddedFrame.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/EmbeddedFrame.java Thu Jun 04 18:49:37 2015 -0700
@@ -552,16 +552,10 @@
}
public void setModalBlocked(Dialog blocker, boolean blocked) {}
- /**
- * @see java.awt.peer.ContainerPeer#restack
- */
public void restack() {
throw new UnsupportedOperationException();
}
- /**
- * @see java.awt.peer.ContainerPeer#isRestackSupported
- */
public boolean isRestackSupported() {
return false;
}
--- a/jdk/src/java.desktop/share/classes/sun/awt/HToolkit.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/HToolkit.java Thu Jun 04 18:49:37 2015 -0700
@@ -45,8 +45,7 @@
* with the HeadlessToolkit. It is primarily used
* in embedded JRE's that do not have sun/awt/X11 classes.
*/
-public class HToolkit extends SunToolkit
- implements ComponentFactory {
+public final class HToolkit extends SunToolkit implements ComponentFactory {
private static final KeyboardFocusManagerPeer kfmPeer = new KeyboardFocusManagerPeer() {
@Override
--- a/jdk/src/java.desktop/share/classes/sun/awt/SunToolkit.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/SunToolkit.java Thu Jun 04 18:49:37 2015 -0700
@@ -366,8 +366,8 @@
* status to synchronous for any of its windows, then further focus
* behaviour is unspecified.
* <p>
- * @param w window for which the lightweight focus request status
- * should be set
+ * @param changed the window for which the lightweight focus request
+ * status should be set
* @param status the value of lightweight focus request status
*/
@@ -1459,9 +1459,9 @@
* <p> Notice that realSync isn't guaranteed to work if recurring
* actions occur, such as if during processing of some event
* another request which may generate some events occurs. By
- * default, sync tries to perform as much as {@value MAX_ITERS}
+ * default, sync tries to perform as much as {@value #MAX_ITERS}
* cycles of event processing, allowing for roughly {@value
- * MAX_ITERS} additional requests.
+ * #MAX_ITERS} additional requests.
*
* <p> For example, requestFocus() generates native request, which
* generates one or two Java focus events, which then generate a
--- a/jdk/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java Thu Jun 04 18:49:37 2015 -0700
@@ -151,7 +151,7 @@
/**
- * @see java.awt.Clipboard#getAvailableDataFlavors
+ * @see java.awt.datatransfer.Clipboard#getAvailableDataFlavors
* @since 1.5
*/
public DataFlavor[] getAvailableDataFlavors() {
@@ -167,7 +167,7 @@
}
/**
- * @see java.awt.Clipboard#isDataFlavorAvailable
+ * @see java.awt.datatransfer.Clipboard#isDataFlavorAvailable
* @since 1.5
*/
public boolean isDataFlavorAvailable(DataFlavor flavor) {
@@ -186,7 +186,7 @@
}
/**
- * @see java.awt.Clipboard#getData
+ * @see java.awt.datatransfer.Clipboard#getData
* @since 1.5
*/
public Object getData(DataFlavor flavor)
--- a/jdk/src/java.desktop/share/classes/sun/awt/geom/PathConsumer2D.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/geom/PathConsumer2D.java Thu Jun 04 18:49:37 2015 -0700
@@ -27,30 +27,30 @@
public interface PathConsumer2D {
/**
- * @see java.awt.geom.Path2D.Float.moveTo
+ * @see java.awt.geom.Path2D.Float#moveTo
*/
public void moveTo(float x, float y);
/**
- * @see java.awt.geom.Path2D.Float.lineTo
+ * @see java.awt.geom.Path2D.Float#lineTo
*/
public void lineTo(float x, float y);
/**
- * @see java.awt.geom.Path2D.Float.quadTo
+ * @see java.awt.geom.Path2D.Float#quadTo
*/
public void quadTo(float x1, float y1,
float x2, float y2);
/**
- * @see java.awt.geom.Path2D.Float.curveTo
+ * @see java.awt.geom.Path2D.Float#curveTo
*/
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3);
/**
- * @see java.awt.geom.Path2D.Float.closePath
+ * @see java.awt.geom.Path2D.Float#closePath
*/
public void closePath();
--- a/jdk/src/java.desktop/share/classes/sun/awt/im/ExecutableInputMethodManager.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/im/ExecutableInputMethodManager.java Thu Jun 04 18:49:37 2015 -0700
@@ -519,7 +519,7 @@
* Writes the preferred input method descriptor class name into
* the user's Preferences tree in accordance with the given locale.
*
- * @param inputMethodLocator input method locator to remember.
+ * @param locator input method locator to remember.
*/
private synchronized void putPreferredInputMethod(InputMethodLocator locator) {
InputMethodDescriptor descriptor = locator.getDescriptor();
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/ByteBandedRaster.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/ByteBandedRaster.java Thu Jun 04 18:49:37 2015 -0700
@@ -176,7 +176,7 @@
* Returns data offset for the specified band. The data offset
* is the index into the band's data array
* in which the first sample of the first scanline is stored.
- * @param The band whose offset is returned.
+ * @param band The band whose offset is returned.
*/
public int getDataOffset(int band) {
return dataOffsets[band];
@@ -222,11 +222,11 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param outData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
- * @return An object reference to an array of type defined by
+ * @return An object reference to an array of type defined by
* getTransferType() with the request pixel data.
*/
public Object getDataElements(int x, int y, Object obj) {
@@ -267,9 +267,9 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
- * @param outData An object reference to an array of type defined by
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -320,8 +320,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
* @param band The band to return.
* @param outData If non-null, data elements for all bands
* at the specified location are returned in this array.
@@ -368,8 +368,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
* @param outData If non-null, data elements for all bands
* at the specified location are returned in this array.
* @return Data array with data elements for all bands.
@@ -412,7 +412,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements()
* containing the pixel data to place at x,y.
*/
@@ -505,7 +505,7 @@
* @param y The Y coordinate of the upper left pixel location.
* @param w Width of the pixel rectangle.
* @param h Height of the pixel rectangle.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements()
* containing the pixel data to place between x,y and
* x+h, y+h.
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/ByteComponentRaster.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/ByteComponentRaster.java Thu Jun 04 18:49:37 2015 -0700
@@ -253,7 +253,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param outData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -299,9 +299,9 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
- * @param outData An object reference to an array of type defined by
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -352,8 +352,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
* @param band The band to return.
* @param outData If non-null, data elements for all bands
* at the specified location are returned in this array.
@@ -415,8 +415,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
* @param outData If non-null, data elements for all bands
* at the specified location are returned in this array.
* @return Data array with data elements for all bands.
@@ -458,7 +458,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements()
* containing the pixel data to place at x,y.
*/
@@ -577,7 +577,7 @@
* @param y The Y coordinate of the upper left pixel location.
* @param w Width of the pixel rectangle.
* @param h Height of the pixel rectangle.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements()
* containing the pixel data to place between x,y and
* x+h, y+h.
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/ByteInterleavedRaster.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/ByteInterleavedRaster.java Thu Jun 04 18:49:37 2015 -0700
@@ -305,7 +305,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param outData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -351,9 +351,9 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
- * @param outData An object reference to an array of type defined by
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -376,8 +376,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
* @param band The band to return.
* @param outData If non-null, data elements for all bands
* at the specified location are returned in this array.
@@ -437,8 +437,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
* @param outData If non-null, data elements for all bands
* at the specified location are returned in this array.
* @return Data array with data elements for all bands.
@@ -536,7 +536,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements()
* containing the pixel data to place at x,y.
*/
@@ -666,7 +666,7 @@
* @param y The Y coordinate of the upper left pixel location.
* @param w Width of the pixel rectangle.
* @param h Height of the pixel rectangle.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements()
* containing the pixel data to place between x,y and
* x+h, y+h.
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/BytePackedRaster.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/BytePackedRaster.java Thu Jun 04 18:49:37 2015 -0700
@@ -234,7 +234,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param outData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -306,9 +306,9 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
- * @param outData An object reference to an array of type defined by
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -358,8 +358,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
* @param band The band to return, is ignored.
* @param outData If non-null, data elements
* at the specified locations are returned in this array.
@@ -383,8 +383,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
* @param outData If non-null, data elements
* at the specified locations are returned in this array.
* @return Byte array with data elements.
@@ -499,7 +499,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements()
* containing the pixel data to place at x,y.
*/
@@ -857,7 +857,7 @@
* @param y The Y coordinate of the upper left pixel location.
* @param w Width of the pixel rectangle.
* @param h Height of the pixel rectangle.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements()
* containing the pixel data to place between x,y and
* x+h, y+h.
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/ImageFetchable.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/ImageFetchable.java Thu Jun 04 18:49:37 2015 -0700
@@ -33,7 +33,7 @@
* threads which manage the applications User Interface.
*
* @see ImageFetcher
- * @see ImageProducer
+ * @see java.awt.image.ImageProducer
*
* @author Jim Graham
*/
@@ -42,7 +42,7 @@
* This method is called by one of the ImageFetcher threads to start
* the flow of information from the ImageProducer to the ImageConsumer.
* @see ImageFetcher
- * @see ImageProducer
+ * @see java.awt.image.ImageProducer
*/
public void doFetch();
}
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/IntegerComponentRaster.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/IntegerComponentRaster.java Thu Jun 04 18:49:37 2015 -0700
@@ -263,7 +263,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param outData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -309,9 +309,9 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
- * @param outData An object reference to an array of type defined by
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -358,7 +358,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements()
* containing the pixel data to place at x,y.
*/
@@ -489,7 +489,7 @@
* @param y The Y coordinate of the upper left pixel location.
* @param w Width of the pixel rectangle.
* @param h Height of the pixel rectangle.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements()
* containing the pixel data to place between x,y and
* x+h, y+h.
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/IntegerInterleavedRaster.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/IntegerInterleavedRaster.java Thu Jun 04 18:49:37 2015 -0700
@@ -206,7 +206,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param outData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -249,9 +249,9 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
- * @param outData An object reference to an array of type defined by
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -291,7 +291,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements()
* containing the pixel data to place at x,y.
*/
@@ -410,7 +410,7 @@
* @param y The Y coordinate of the upper left pixel location.
* @param w Width of the pixel rectangle.
* @param h Height of the pixel rectangle.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements()
* containing the pixel data to place between x,y and
* x+h, y+h.
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/ShortBandedRaster.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/ShortBandedRaster.java Thu Jun 04 18:49:37 2015 -0700
@@ -172,7 +172,7 @@
* Returns the data offset for the specified band. The data offset
* is the index into the band's data array
* in which the first sample of the first scanline is stored.
- * @param The band whose offset is returned.
+ * @param band The band whose offset is returned.
*/
public int getDataOffset(int band) {
return dataOffsets[band];
@@ -218,7 +218,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param outData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -262,9 +262,9 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
- * @param outData An object reference to an array of type defined by
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -315,8 +315,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
* @param band The band to return.
* @param outData If non-null, data elements for all bands
* at the specified location are returned in this array.
@@ -363,8 +363,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
* @param outData If non-null, data elements for all bands
* at the specified location are returned in this array.
* @return Data array with data elements for all bands.
@@ -407,7 +407,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements()
* containing the pixel data to place at x,y.
*/
@@ -503,7 +503,7 @@
* @param y The Y coordinate of the upper left pixel location.
* @param w Width of the pixel rectangle.
* @param h Height of the pixel rectangle.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements()
* containing the pixel data to place between x,y and
* x+h, y+h.
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/ShortComponentRaster.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/ShortComponentRaster.java Thu Jun 04 18:49:37 2015 -0700
@@ -252,7 +252,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param outData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -298,9 +298,9 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
- * @param outData An object reference to an array of type defined by
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -351,8 +351,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the sample rectangle.
- * @param height Height of the sample rectangle.
+ * @param w Width of the sample rectangle.
+ * @param h Height of the sample rectangle.
* @param band The band to return.
* @param outData If non-null, data elements for all bands
* at the specified location are returned in this array.
@@ -414,8 +414,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
* @param outData If non-null, data elements for all bands
* at the specified location are returned in this array.
* @return Data array with data elements for all bands.
@@ -456,7 +456,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements()
* containing the pixel data to place at x,y.
*/
@@ -553,7 +553,7 @@
* @param y The Y coordinate of the upper left pixel location.
* @param w Width of the pixel rectangle.
* @param h Height of the pixel rectangle.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements()
* containing the pixel data to place between x,y and
* x+h, y+h.
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/ShortInterleavedRaster.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/ShortInterleavedRaster.java Thu Jun 04 18:49:37 2015 -0700
@@ -225,7 +225,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param outData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -271,9 +271,9 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
- * @param outData An object reference to an array of type defined by
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements().
* If null an array of appropriate type and size will be
* allocated.
@@ -324,8 +324,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the sample rectangle.
- * @param height Height of the sample rectangle.
+ * @param w Width of the sample rectangle.
+ * @param h Height of the sample rectangle.
* @param band The band to return.
* @param outData If non-null, data elements for all bands
* at the specified location are returned in this array.
@@ -387,8 +387,8 @@
* </pre>
* @param x The X coordinate of the upper left pixel location.
* @param y The Y coordinate of the upper left pixel location.
- * @param width Width of the pixel rectangle.
- * @param height Height of the pixel rectangle.
+ * @param w Width of the pixel rectangle.
+ * @param h Height of the pixel rectangle.
* @param outData If non-null, data elements for all bands
* at the specified location are returned in this array.
* @return Data array with data elements for all bands.
@@ -429,7 +429,7 @@
* and references anything other than an array of transferType.
* @param x The X coordinate of the pixel location.
* @param y The Y coordinate of the pixel location.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length getNumDataElements()
* containing the pixel data to place at x,y.
*/
@@ -525,7 +525,7 @@
* @param y The Y coordinate of the upper left pixel location.
* @param w Width of the pixel rectangle.
* @param h Height of the pixel rectangle.
- * @param inData An object reference to an array of type defined by
+ * @param obj An object reference to an array of type defined by
* getTransferType() and length w*h*getNumDataElements()
* containing the pixel data to place between x,y and
* x+h, y+h.
--- a/jdk/src/java.desktop/share/classes/sun/awt/shell/ShellFolderColumnInfo.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/shell/ShellFolderColumnInfo.java Thu Jun 04 18:49:37 2015 -0700
@@ -40,7 +40,7 @@
private SortOrder sortOrder;
private Comparator<?> comparator;
/**
- * <code>false</code> (default) if the {@link comparator} expects folders as arguments,
+ * <code>false</code> (default) if the {@link #comparator} expects folders as arguments,
* and <code>true</code> if folder's column values. The first option is used default for comparison
* on Windows and also for separating files from directories when sorting using
* ShellFolderManager's inner comparator.
--- a/jdk/src/java.desktop/share/classes/sun/awt/util/IdentityArrayList.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/util/IdentityArrayList.java Thu Jun 04 18:49:37 2015 -0700
@@ -68,7 +68,7 @@
* synchronizing on some object that naturally encapsulates the list.
*
* If no such object exists, the list should be "wrapped" using the
- * {@link Collections#synchronizedList Collections.synchronizedList}
+ * {@link java.util.Collections#synchronizedList Collections.synchronizedList}
* method. This is best done at creation time, to prevent accidental
* unsynchronized access to the list:<pre>
* List list = Collections.synchronizedList(new IdentityArrayList(...));</pre>
--- a/jdk/src/java.desktop/share/classes/sun/awt/util/IdentityLinkedList.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/awt/util/IdentityLinkedList.java Thu Jun 04 18:49:37 2015 -0700
@@ -41,7 +41,7 @@
* the <tt>IdentityLinkedList</tt> class provides uniformly named methods to
* <tt>get</tt>, <tt>remove</tt> and <tt>insert</tt> an element at the
* beginning and end of the list. These operations allow linked lists to be
- * used as a stack, {@linkplain Queue queue}, or {@linkplain Deque
+ * used as a stack, {@linkplain java.util.Queue queue}, or {@linkplain Deque
* double-ended queue}. <p>
*
* The class implements the <tt>Deque</tt> interface, providing
@@ -62,7 +62,7 @@
* encapsulates the list.
*
* If no such object exists, the list should be "wrapped" using the
- * {@link Collections#synchronizedList Collections.synchronizedList}
+ * {@link java.util.Collections#synchronizedList Collections.synchronizedList}
* method. This is best done at creation time, to prevent accidental
* unsynchronized access to the list:<pre>
* List list = Collections.synchronizedList(new IdentityLinkedList(...));</pre>
@@ -478,7 +478,7 @@
* Adds the specified element as the tail (last element) of this list.
*
* @param e the element to add
- * @return <tt>true</tt> (as specified by {@link Queue#offer})
+ * @return <tt>true</tt> (as specified by {@link java.util.Queue#offer})
* @since 1.5
*/
public boolean offer(E e) {
--- a/jdk/src/java.desktop/share/classes/sun/font/ScriptRun.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/font/ScriptRun.java Thu Jun 04 18:49:37 2015 -0700
@@ -138,7 +138,7 @@
* Get the script code for the script of the current script run.
*
* @return the script code for the script of the current script run.
- * @see #Script
+ * @see Script
*/
public int getScriptCode() {
return scriptCode;
@@ -274,7 +274,7 @@
* @param scriptOne one of the script codes.
* @param scriptTwo the other script code.
* @return <code>true</code> if the two scripts are the same.
- * @see com.ibm.icu.lang.Script
+ * @see Script
*/
private static boolean sameScript(int scriptOne, int scriptTwo) {
return scriptOne == scriptTwo || scriptOne <= Script.INHERITED || scriptTwo <= Script.INHERITED;
--- a/jdk/src/java.desktop/share/classes/sun/font/StandardTextSource.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/font/StandardTextSource.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,42 +33,43 @@
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
-public class StandardTextSource extends TextSource {
- char[] chars;
- int start;
- int len;
- int cstart;
- int clen;
- int level; // assumed all uniform
- int flags; // see GlyphVector.java
- Font font;
- FontRenderContext frc;
- CoreMetrics cm;
+final class StandardTextSource extends TextSource {
+
+ private final char[] chars;
+ private final int start;
+ private final int len;
+ private final int cstart;
+ private final int clen;
+ private final int level; // assumed all uniform
+ private final int flags; // see GlyphVector.java
+ private final Font font;
+ private final FontRenderContext frc;
+ private final CoreMetrics cm;
- /**
- * Create a simple implementation of a TextSource.
- *
- * Chars is an array containing clen chars in the context, in
- * logical order, contiguously starting at cstart. Start and len
- * represent that portion of the context representing the true
- * source; start, like cstart, is relative to the start of the
- * character array.
- *
- * Level is the bidi level (0-63 for the entire context. Flags is
- * the layout flags. Font is the font, frc is the render context,
- * and lm is the line metrics for the entire source text, but not
- * necessarily the context.
- */
- public StandardTextSource(char[] chars,
- int start,
- int len,
- int cstart,
- int clen,
- int level,
- int flags,
- Font font,
- FontRenderContext frc,
- CoreMetrics cm) {
+ /**
+ * Create a simple implementation of a TextSource.
+ *
+ * Chars is an array containing clen chars in the context, in
+ * logical order, contiguously starting at cstart. Start and len
+ * represent that portion of the context representing the true
+ * source; start, like cstart, is relative to the start of the
+ * character array.
+ *
+ * Level is the bidi level (0-63 for the entire context. Flags is
+ * the layout flags. Font is the font, frc is the render context,
+ * and lm is the line metrics for the entire source text, but not
+ * necessarily the context.
+ */
+ StandardTextSource(char[] chars,
+ int start,
+ int len,
+ int cstart,
+ int clen,
+ int level,
+ int flags,
+ Font font,
+ FontRenderContext frc,
+ CoreMetrics cm) {
if (chars == null) {
throw new IllegalArgumentException("bad chars: null");
}
@@ -97,7 +98,7 @@
throw new IllegalArgumentException("bad frc: null");
}
- this.chars = chars.clone();
+ this.chars = chars;
this.start = start;
this.len = len;
this.cstart = cstart;
@@ -115,40 +116,10 @@
}
}
- /** Create a StandardTextSource whose context is coextensive with the source. */
- public StandardTextSource(char[] chars,
- int start,
- int len,
- int level,
- int flags,
- Font font,
- FontRenderContext frc,
- CoreMetrics cm) {
- this(chars, start, len, start, len, level, flags, font, frc, cm);
- }
-
- /** Create a StandardTextSource whose context and source are coextensive with the entire char array. */
- public StandardTextSource(char[] chars,
- int level,
- int flags,
- Font font,
- FontRenderContext frc) {
- this(chars, 0, chars.length, 0, chars.length, level, flags, font, frc, null);
- }
-
- /** Create a StandardTextSource whose context and source are all the text in the String. */
- public StandardTextSource(String str,
- int level,
- int flags,
- Font font,
- FontRenderContext frc) {
- this(str.toCharArray(), 0, str.length(), 0, str.length(), level, flags, font, frc, null);
- }
-
// TextSource API
public char[] getChars() {
- return chars.clone();
+ return chars;
}
public int getStart() {
--- a/jdk/src/java.desktop/share/classes/sun/font/TextLabelFactory.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/font/TextLabelFactory.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -41,19 +41,19 @@
*
* @see Font
* @see FontRenderContext
- * @see GlyphVector
+ * @see java.awt.font.GlyphVector
* @see TextLabel
* @see ExtendedTextLabel
* @see Bidi
- * @see TextLayout
+ * @see java.awt.font.TextLayout
*/
-public class TextLabelFactory {
- private FontRenderContext frc;
- private char[] text;
- private Bidi bidi;
+public final class TextLabelFactory {
+ private final FontRenderContext frc;
+ private final char[] text;
+ private final Bidi bidi;
private Bidi lineBidi;
- private int flags;
+ private final int flags;
private int lineStart;
private int lineLimit;
--- a/jdk/src/java.desktop/share/classes/sun/java2d/NullSurfaceData.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/NullSurfaceData.java Thu Jun 04 18:49:37 2015 -0700
@@ -86,7 +86,7 @@
* In most cases, the returned Raster might contain more pixels
* than requested.
*
- * @see useTightBBoxes
+ * @see #useTightBBoxes
*/
public Raster getRaster(int x, int y, int w, int h) {
throw new InvalidPipeException("should be NOP");
@@ -101,7 +101,7 @@
* the pixels has to be made when doing a getRaster. The
* fewer pixels copied, the faster the operation will go.
*
- * @see getRaster
+ * @see #getRaster
*/
public boolean useTightBBoxes() {
return false;
--- a/jdk/src/java.desktop/share/classes/sun/java2d/SunCompositeContext.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/SunCompositeContext.java Thu Jun 04 18:49:37 2015 -0700
@@ -88,13 +88,13 @@
* @param src2 The second source tile for the compositing operation.
* @param dst The tile where the result of the operation is stored.
*/
- public void compose(Raster srcArg, Raster dstIn, WritableRaster dstOut) {
+ public void compose(Raster src1, Raster src2, WritableRaster dst) {
WritableRaster src;
int w;
int h;
- if (dstIn != dstOut) {
- dstOut.setDataElements(0, 0, dstIn);
+ if (src2 != dst) {
+ dst.setDataElements(0, 0, src2);
}
// REMIND: We should be able to create a SurfaceData from just
@@ -102,20 +102,20 @@
// create a SurfaceData from a BufferedImage then we need to
// make a WritableRaster since it is needed to construct a
// BufferedImage.
- if (srcArg instanceof WritableRaster) {
- src = (WritableRaster) srcArg;
+ if (src1 instanceof WritableRaster) {
+ src = (WritableRaster) src1;
} else {
- src = srcArg.createCompatibleWritableRaster();
- src.setDataElements(0, 0, srcArg);
+ src = src1.createCompatibleWritableRaster();
+ src.setDataElements(0, 0, src1);
}
- w = Math.min(src.getWidth(), dstIn.getWidth());
- h = Math.min(src.getHeight(), dstIn.getHeight());
+ w = Math.min(src.getWidth(), src2.getWidth());
+ h = Math.min(src.getHeight(), src2.getHeight());
BufferedImage srcImg = new BufferedImage(srcCM, src,
srcCM.isAlphaPremultiplied(),
null);
- BufferedImage dstImg = new BufferedImage(dstCM, dstOut,
+ BufferedImage dstImg = new BufferedImage(dstCM, dst,
dstCM.isAlphaPremultiplied(),
null);
--- a/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java Thu Jun 04 18:49:37 2015 -0700
@@ -874,13 +874,13 @@
* space. The rendering attributes taken into account include the
* clip, transform, and stroke attributes.
* @param rect The area in device space to check for a hit.
- * @param p The path to check for a hit.
+ * @param s The path to check for a hit.
* @param onStroke Flag to choose between testing the stroked or
* the filled path.
* @return True if there is a hit, false otherwise.
* @see #setStroke
- * @see #fillPath
- * @see #drawPath
+ * @see #fill(Shape)
+ * @see #draw(Shape)
* @see #transform
* @see #setTransform
* @see #clip
@@ -1295,7 +1295,7 @@
/**
* Returns the preferences for the rendering algorithms.
- * @param hintCategory The category of hint to be set. The strings
+ * @param hintKey The category of hint to be set. The strings
* are defined in the RenderingHints class.
* @return The preferences for rendering algorithms. The strings
* are defined in the RenderingHints class.
@@ -1577,7 +1577,7 @@
* Cx'(p) = Cx(Tx(p)).
* A copy of the Tx is made, if necessary, so further
* modifications to Tx do not affect rendering.
- * @param Tx The Transform object to be composed with the current
+ * @param xform The Transform object to be composed with the current
* transform.
* @see #setTransform
* @see AffineTransform
@@ -1606,7 +1606,6 @@
* Sets the Transform in the current graphics state.
* @param Tx The Transform object to be used in the rendering process.
* @see #transform
- * @see TransformChain
* @see AffineTransform
*/
@Override
@@ -1789,8 +1788,8 @@
* of the component, use appropriate methods of the component.
* @param color The background color that should be used in
* subsequent calls to clearRect().
- * @see getBackground
- * @see Graphics.clearRect()
+ * @see #getBackground
+ * @see Graphics#clearRect
*/
public void setBackground(Color color) {
backgroundColor = color;
@@ -1798,7 +1797,7 @@
/**
* Returns the background color used for clearing a region.
- * @see setBackground
+ * @see #setBackground
*/
public Color getBackground() {
return backgroundColor;
@@ -1806,7 +1805,7 @@
/**
* Returns the current Stroke in the Graphics2D state.
- * @see setStroke
+ * @see #setStroke
*/
public Stroke getStroke() {
return stroke;
@@ -2056,7 +2055,7 @@
* with the current transform in the Graphics2D state before being
* intersected with the current clip. This method is used to make the
* current clip smaller. To make the clip larger, use any setClip method.
- * @param p The Path to be intersected with the current clip.
+ * @param s The Path to be intersected with the current clip.
*/
public void clip(Shape s) {
s = transformShape(s);
@@ -2483,7 +2482,7 @@
* Strokes the outline of a Path using the settings of the current
* graphics state. The rendering attributes applied include the
* clip, transform, paint or color, composite and stroke attributes.
- * @param p The path to be drawn.
+ * @param s The path to be drawn.
* @see #setStroke
* @see #setPaint
* @see java.awt.Graphics#setColor
--- a/jdk/src/java.desktop/share/classes/sun/java2d/SurfaceData.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/SurfaceData.java Thu Jun 04 18:49:37 2015 -0700
@@ -939,7 +939,7 @@
* In most cases, the returned Raster might contain more pixels
* than requested.
*
- * @see useTightBBoxes
+ * @see #useTightBBoxes
*/
public abstract Raster getRaster(int x, int y, int w, int h);
@@ -952,7 +952,7 @@
* the pixels has to be made when doing a getRaster. The
* fewer pixels copied, the faster the operation will go.
*
- * @see getRaster
+ * @see #getRaster
*/
public boolean useTightBBoxes() {
// Note: The native equivalent would trigger on VISIBLE_TO_NATIVE
--- a/jdk/src/java.desktop/share/classes/sun/java2d/pipe/PixelToParallelogramConverter.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/pipe/PixelToParallelogramConverter.java Thu Jun 04 18:49:37 2015 -0700
@@ -56,7 +56,7 @@
* @param minPenSize minimum pen size for dropout control
* @param normPosition sub-pixel location to normalize endpoints
* for STROKE_NORMALIZE cases
- * @param adjustFill boolean to control whethere normalization
+ * @param adjustfill boolean to control whethere normalization
* constants are also applied to fill operations
* (normally true for non-AA, false for AA)
*/
--- a/jdk/src/java.desktop/share/classes/sun/java2d/pipe/RenderingEngine.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/pipe/RenderingEngine.java Thu Jun 04 18:49:37 2015 -0700
@@ -66,7 +66,7 @@
* line width can get before dropouts occur. Rendering with a BasicStroke
* is defined to never allow the line to have breaks, gaps, or dropouts
* even if the width is set to 0.0f, so this information allows the
- * {@link SunGraphics2D} class to detect the "thin line" case and set
+ * {@link sun.java2d.SunGraphics2D} class to detect the "thin line" case and set
* the rendering attributes accordingly.
* </dl>
* At startup the runtime will load a single instance of this class.
@@ -177,11 +177,11 @@
* The specified {@code src} {@link Shape} is widened according
* to the parameters specified by the {@link BasicStroke} object.
* Adjustments are made to the path as appropriate for the
- * {@link VALUE_STROKE_NORMALIZE} hint if the {@code normalize}
- * boolean parameter is true.
+ * {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the
+ * {@code normalize} boolean parameter is true.
* Adjustments are made to the path as appropriate for the
- * {@link VALUE_ANTIALIAS_ON} hint if the {@code antialias}
- * boolean parameter is true.
+ * {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the
+ * {@code antialias} boolean parameter is true.
* <p>
* The geometry of the widened path is forwarded to the indicated
* {@link PathConsumer2D} object as it is calculated.
--- a/jdk/src/java.desktop/share/classes/sun/java2d/pipe/hw/AccelDeviceEventNotifier.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/pipe/hw/AccelDeviceEventNotifier.java Thu Jun 04 18:49:37 2015 -0700
@@ -137,7 +137,7 @@
*
* @param screen a screen number with which the device which is a source of
* the event is associated with
- * @param eventType a type of the event
+ * @param deviceEventType a type of the event
* @see #DEVICE_DISPOSED
* @see #DEVICE_RESET
*/
--- a/jdk/src/java.desktop/share/classes/sun/java2d/pipe/hw/AccelGraphicsConfig.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/pipe/hw/AccelGraphicsConfig.java Thu Jun 04 18:49:37 2015 -0700
@@ -77,7 +77,7 @@
* events.
*
* Note: a hard link to the listener may be kept so it must be explicitly
- * removed via {@link #removeDeviceEventListener()}.
+ * removed via {@link #removeDeviceEventListener}.
*
* @param l the listener
* @see AccelDeviceEventListener
--- a/jdk/src/java.desktop/share/classes/sun/java2d/pipe/hw/ContextCapabilities.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/pipe/hw/ContextCapabilities.java Thu Jun 04 18:49:37 2015 -0700
@@ -65,7 +65,7 @@
/**
* Constructs a {@code ContextCapabilities} object.
* @param caps an {@code int} representing the capabilities
- * @param a {@code String} representing the name of the adapter, or null,
+ * @param adapterId {@code String} representing the name of the adapter, or null,
* in which case the adapterId will be set to "unknown adapter".
*/
protected ContextCapabilities(int caps, String adapterId) {
--- a/jdk/src/java.desktop/share/classes/sun/java2d/pisces/PiscesCache.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/pisces/PiscesCache.java Thu Jun 04 18:49:37 2015 -0700
@@ -29,8 +29,6 @@
/**
* An object used to cache pre-rendered complex paths.
- *
- * @see PiscesRenderer#render
*/
final class PiscesCache {
--- a/jdk/src/java.desktop/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java Thu Jun 04 18:49:37 2015 -0700
@@ -108,11 +108,11 @@
* The specified {@code src} {@link Shape} is widened according
* to the parameters specified by the {@link BasicStroke} object.
* Adjustments are made to the path as appropriate for the
- * {@link VALUE_STROKE_NORMALIZE} hint if the {@code normalize}
- * boolean parameter is true.
+ * {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the
+ * {@code normalize} boolean parameter is true.
* Adjustments are made to the path as appropriate for the
- * {@link VALUE_ANTIALIAS_ON} hint if the {@code antialias}
- * boolean parameter is true.
+ * {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the
+ * {@code antialias} boolean parameter is true.
* <p>
* The geometry of the widened path is forwarded to the indicated
* {@link PathConsumer2D} object as it is calculated.
--- a/jdk/src/java.desktop/share/classes/sun/print/DialogOwner.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/print/DialogOwner.java Thu Jun 04 18:49:37 2015 -0700
@@ -45,10 +45,9 @@
private Frame dlgOwner;
/**
- * Construct a new dialog type selection enumeration value with the
- * given integer value.
+ * Construct a new dialog owner attribute with the given frame.
*
- * @param value Integer value.
+ * @param frame the frame that owns the print dialog
*/
public DialogOwner(Frame frame) {
dlgOwner = frame;
--- a/jdk/src/java.desktop/share/classes/sun/print/OpenBook.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/print/OpenBook.java Thu Jun 04 18:49:37 2015 -0700
@@ -74,8 +74,8 @@
/**
* Return the PageFormat of the page specified by 'pageIndex'.
- * @param int The zero based index of the page whose
- * PageFormat is being requested.
+ * @param pageIndex The zero based index of the page whose
+ * PageFormat is being requested.
* @return The PageFormat describing the size and orientation
*/
public PageFormat getPageFormat(int pageIndex) {
@@ -85,8 +85,8 @@
/**
* Return the Printable instance responsible for rendering
* the page specified by 'pageIndex'.
- * @param int The zero based index of the page whose
- * Printable is being requested.
+ * @param pageIndex The zero based index of the page whose
+ * Printable is being requested.
* @return The Printable that will draw the page.
*/
public Printable getPrintable(int pageIndex)
--- a/jdk/src/java.desktop/share/classes/sun/print/PSPathGraphics.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/print/PSPathGraphics.java Thu Jun 04 18:49:37 2015 -0700
@@ -126,7 +126,7 @@
* such as Hebrew and Arabic, the glyphs can be rendered from right to
* left, in which case the coordinate supplied is the location of the
* leftmost character on the baseline.
- * @param s the <code>String</code> to be rendered
+ * @param str the <code>String</code> to be rendered
* @param x, y the coordinates where the <code>String</code>
* should be rendered
* @see #setPaint
@@ -256,7 +256,7 @@
* is transformed by the supplied AffineTransform and
* drawn using PS to the printer context.
*
- * @param img The image to be drawn.
+ * @param image The image to be drawn.
* This method does nothing if <code>img</code> is null.
* @param xform Used to transform the image before drawing.
* This can be null.
--- a/jdk/src/java.desktop/share/classes/sun/print/PeekGraphics.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/print/PeekGraphics.java Thu Jun 04 18:49:37 2015 -0700
@@ -361,9 +361,9 @@
* use this font.
* @param font the font.
* @see java.awt.Graphics#getFont
- * @see java.awt.Graphics#drawChars(java.lang.String, int, int)
- * @see java.awt.Graphics#drawString(byte[], int, int, int, int)
- * @see java.awt.Graphics#drawBytes(char[], int, int, int, int)
+ * @see java.awt.Graphics#drawChars(char[], int, int, int, int)
+ * @see java.awt.Graphics#drawString(String, int, int)
+ * @see java.awt.Graphics#drawBytes(byte[], int, int, int, int)
* @since 1.0
*/
public void setFont(Font font) {
@@ -1446,7 +1446,7 @@
* Draws a string of text.
* The rendering attributes applied include the clip, transform,
* paint or color, font and composite attributes.
- * @param s The string to be drawn.
+ * @param str The string to be drawn.
* @param x,y The coordinates where the string should be drawn.
* @see #setPaint
* @see java.awt.Graphics#setColor
@@ -1548,7 +1548,7 @@
* @param comp The Composite object to be used for drawing.
* @see java.awt.Graphics#setXORMode
* @see java.awt.Graphics#setPaintMode
- * @see AlphaComposite
+ * @see java.awt.AlphaComposite
*/
public void setComposite(Composite comp) {
mGraphics.setComposite(comp);
@@ -1560,8 +1560,8 @@
* @param paint The Paint object to be used to generate color in
* the rendering process.
* @see java.awt.Graphics#setColor
- * @see GradientPaint
- * @see TexturePaint
+ * @see java.awt.GradientPaint
+ * @see java.awt.TexturePaint
*/
public void setPaint(Paint paint) {
mGraphics.setPaint(paint);
@@ -1594,7 +1594,7 @@
* Returns the preferences for the rendering algorithms.
* @param hintCategory The category of hint to be set.
* @return The preferences for rendering algorithms.
- * @see RenderingHings
+ * @see RenderingHints
*/
public Object getRenderingHint(Key hintCategory) {
return mGraphics.getRenderingHint(hintCategory);
@@ -1647,7 +1647,6 @@
* @param Tx The Transform object to be composed with the current
* transform.
* @see #setTransform
- * @see TransformChain
* @see AffineTransform
*/
public void transform(AffineTransform Tx) {
@@ -1658,7 +1657,6 @@
* Sets the Transform in the current graphics state.
* @param Tx The Transform object to be used in the rendering process.
* @see #transform
- * @see TransformChain
* @see AffineTransform
*/
public void setTransform(AffineTransform Tx) {
@@ -1700,8 +1698,8 @@
* of the component, use appropriate methods of the component.
* @param color The background color that should be used in
* subsequent calls to clearRect().
- * @see getBackground
- * @see Graphics.clearRect()
+ * @see #getBackground
+ * @see Graphics#clearRect
*/
public void setBackground(Color color) {
mGraphics.setBackground(color);
@@ -1709,7 +1707,7 @@
/**
* Returns the background color used for clearing a region.
- * @see setBackground
+ * @see #setBackground
*/
public Color getBackground() {
return mGraphics.getBackground();
@@ -1717,7 +1715,7 @@
/**
* Returns the current Stroke in the Graphics2D state.
- * @see setStroke
+ * @see #setStroke
*/
public Stroke getStroke() {
return mGraphics.getStroke();
--- a/jdk/src/java.desktop/share/classes/sun/print/PrintJob2D.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/print/PrintJob2D.java Thu Jun 04 18:49:37 2015 -0700
@@ -79,7 +79,7 @@
* A class which initiates and executes a print job using
* the underlying PrinterJob graphics conversions.
*
- * @see Toolkit#getPrintJob
+ * @see java.awt.Toolkit#getPrintJob
*
*/
public class PrintJob2D extends PrintJob implements Printable, Runnable {
@@ -750,7 +750,7 @@
* The page is sent to the printer when the graphics
* object is disposed. This graphics object will also implement
* the PrintGraphics interface.
- * @see PrintGraphics
+ * @see java.awt.PrintGraphics
*/
public Graphics getGraphics() {
@@ -937,7 +937,7 @@
* If the requested page does not exist then this method returns
* NO_SUCH_PAGE; otherwise PAGE_EXISTS is returned.
* The <code>Graphics</code> class or subclass implements the
- * {@link PrinterGraphics} interface to provide additional
+ * {@link java.awt.PrintGraphics} interface to provide additional
* information. If the <code>Printable</code> object
* aborts the print job then it throws a {@link PrinterException}.
* @param graphics the context into which the page is drawn
--- a/jdk/src/java.desktop/share/classes/sun/print/ProxyGraphics2D.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/print/ProxyGraphics2D.java Thu Jun 04 18:49:37 2015 -0700
@@ -297,9 +297,9 @@
* use this font.
* @param font the font.
* @see java.awt.Graphics#getFont
- * @see java.awt.Graphics#drawChars(java.lang.String, int, int)
- * @see java.awt.Graphics#drawString(byte[], int, int, int, int)
- * @see java.awt.Graphics#drawBytes(char[], int, int, int, int)
+ * @see java.awt.Graphics#drawChars(char[], int, int, int, int)
+ * @see java.awt.Graphics#drawString(String, int, int)
+ * @see java.awt.Graphics#drawBytes(byte[], int, int, int, int)
* @since 1.0
*/
public void setFont(Font font) {
@@ -1345,7 +1345,7 @@
* Draws a string of text.
* The rendering attributes applied include the clip, transform,
* paint or color, font and composite attributes.
- * @param s The string to be drawn.
+ * @param str The string to be drawn.
* @param x,y The coordinates where the string should be drawn.
* @see #setPaint
* @see java.awt.Graphics#setColor
@@ -1432,7 +1432,7 @@
* @param comp The Composite object to be used for drawing.
* @see java.awt.Graphics#setXORMode
* @see java.awt.Graphics#setPaintMode
- * @see AlphaComposite
+ * @see java.awt.AlphaComposite
*/
public void setComposite(Composite comp) {
mGraphics.setComposite(comp);
@@ -1444,8 +1444,8 @@
* @param paint The Paint object to be used to generate color in
* the rendering process.
* @see java.awt.Graphics#setColor
- * @see GradientPaint
- * @see TexturePaint
+ * @see java.awt.GradientPaint
+ * @see java.awt.TexturePaint
*/
public void setPaint(Paint paint) {
mGraphics.setPaint(paint);
@@ -1455,7 +1455,7 @@
* Sets the Stroke in the current graphics state.
* @param s The Stroke object to be used to stroke a Shape in
* the rendering process.
- * @see BasicStroke
+ * @see java.awt.BasicStroke
*/
public void setStroke(Stroke s) {
mGraphics.setStroke(s);
@@ -1478,7 +1478,7 @@
* Returns the preferences for the rendering algorithms.
* @param hintCategory The category of hint to be set.
* @return The preferences for rendering algorithms.
- * @see RenderingHings
+ * @see RenderingHints
*/
public Object getRenderingHint(Key hintCategory) {
return mGraphics.getRenderingHint(hintCategory);
@@ -1531,7 +1531,6 @@
* @param Tx The Transform object to be composed with the current
* transform.
* @see #setTransform
- * @see TransformChain
* @see AffineTransform
*/
public void transform(AffineTransform Tx) {
@@ -1542,7 +1541,6 @@
* Sets the Transform in the current graphics state.
* @param Tx The Transform object to be used in the rendering process.
* @see #transform
- * @see TransformChain
* @see AffineTransform
*/
public void setTransform(AffineTransform Tx) {
@@ -1584,8 +1582,8 @@
* of the component, use appropriate methods of the component.
* @param color The background color that should be used in
* subsequent calls to clearRect().
- * @see getBackground
- * @see Graphics.clearRect()
+ * @see #getBackground
+ * @see Graphics#clearRect
*/
public void setBackground(Color color) {
mGraphics.setBackground(color);
@@ -1593,7 +1591,7 @@
/**
* Returns the background color used for clearing a region.
- * @see setBackground
+ * @see #setBackground
*/
public Color getBackground() {
return mGraphics.getBackground();
@@ -1601,7 +1599,7 @@
/**
* Returns the current Stroke in the Graphics2D state.
- * @see setStroke
+ * @see #setStroke
*/
public Stroke getStroke() {
return mGraphics.getStroke();
--- a/jdk/src/java.desktop/share/classes/sun/print/ProxyPrintGraphics.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/print/ProxyPrintGraphics.java Thu Jun 04 18:49:37 2015 -0700
@@ -69,7 +69,7 @@
* <code>Graphics</code> object, but with a new translation and
* clip area.
* Refer to
- * {@link sun.print.ProxyGraphics#createGraphics}
+ * {@link sun.print.ProxyGraphics#create(int, int, int, int)}
* for a complete description of this method.
* <p>
* @param x the <i>x</i> coordinate.
--- a/jdk/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java Thu Jun 04 18:49:37 2015 -0700
@@ -498,7 +498,7 @@
* Throws <code>PrinterException</code> if the specified service
* cannot support the <code>Pageable</code> and
* <code>Printable</code> interfaces necessary to support 2D printing.
- * @param a print service which supports 2D printing.
+ * @param service print service which supports 2D printing.
*
* @throws PrinterException if the specified service does not support
* 2D printing or no longer available.
@@ -1024,7 +1024,7 @@
* The pages in the document to be printed by this PrinterJob
* are drawn by the Printable object 'painter'. The PageFormat
* for each page is the default page format.
- * @param Printable Called to render each page of the document.
+ * @param painter Called to render each page of the document.
*/
public void setPrintable(Printable painter) {
setPageable(new OpenBook(defaultPage(new PageFormat()), painter));
@@ -1034,9 +1034,9 @@
* The pages in the document to be printed by this PrinterJob
* are drawn by the Printable object 'painter'. The PageFormat
* of each page is 'format'.
- * @param Printable Called to render each page of the document.
- * @param PageFormat The size and orientation of each page to
- * be printed.
+ * @param painter Called to render each page of the document.
+ * @param format The size and orientation of each page to
+ * be printed.
*/
public void setPrintable(Printable painter, PageFormat format) {
setPageable(new OpenBook(format, painter));
@@ -1048,7 +1048,7 @@
* Pageable instance 'document'. 'document' will be queried
* for the number of pages as well as the PageFormat and
* Printable for each page.
- * @param Pageable The document to be printed. It may not be null.
+ * @param document The document to be printed. It may not be null.
* @exception NullPointerException the Pageable passed in was null.
* @see PageFormat
* @see Printable
--- a/jdk/src/java.desktop/share/classes/sun/swing/CachedPainter.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/swing/CachedPainter.java Thu Jun 04 18:49:37 2015 -0700
@@ -89,7 +89,7 @@
* @param y Y-coordinate to render to
* @param w Width to render in
* @param h Height to render in
- * @param arg Variable arguments that will be passed to paintToImage
+ * @param args Variable arguments that will be passed to paintToImage
*/
public void paint(Component c, Graphics g, int x,
int y, int w, int h, Object... args) {
--- a/jdk/src/java.desktop/share/classes/sun/swing/UIAction.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/swing/UIAction.java Thu Jun 04 18:49:37 2015 -0700
@@ -54,7 +54,6 @@
* <code>isEnabled(Component)</code>, and be aware that the passed in
* <code>Component</code> may be null.
*
- * @see com.sun.java.swing.ExtendedAction
* @see javax.swing.Action
* @author Scott Violet
*/
--- a/jdk/src/java.desktop/share/classes/sun/swing/plaf/synth/DefaultSynthStyle.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/swing/plaf/synth/DefaultSynthStyle.java Thu Jun 04 18:49:37 2015 -0700
@@ -289,7 +289,7 @@
/**
* Sets the insets.
*
- * @param Insets.
+ * @param insets the new insets.
*/
public void setInsets(Insets insets) {
this.insets = insets;
@@ -300,7 +300,7 @@
* insets will be placed in it, otherwise a new Insets object will be
* created and returned.
*
- * @param context SynthContext identifying requestor
+ * @param state SynthContext identifying requestor
* @param to Where to place Insets
* @return Insets.
*/
@@ -435,7 +435,7 @@
* Returns the default value for a particular property. This is only
* invoked if this style doesn't define a property for <code>key</code>.
*
- * @param state SynthContext identifying requestor
+ * @param context SynthContext identifying requestor
* @param key Property being requested.
* @return Value of the named property
*/
@@ -724,8 +724,6 @@
*
* @param state Component state(s) that this StateInfo should be used
* for
- * @param painter Painter responsible for rendering
- * @param bgPainter Painter responsible for rendering the background
* @param font Font for this state
* @param colors Colors for this state
*/
--- a/jdk/src/java.desktop/share/classes/sun/swing/plaf/synth/Paint9Painter.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/swing/plaf/synth/Paint9Painter.java Thu Jun 04 18:49:37 2015 -0700
@@ -118,7 +118,7 @@
* @param dInsets Destination insets specifying the portion of the image
* will be stretched or tiled, if <code>null</code> empty
* <code>Insets</code> will be used.
- * @param paintType Specifies what type of algorithm to use in painting
+ * @param type Specifies what type of algorithm to use in painting
* @param mask Specifies portion of image to render, if
* <code>PAINT_ALL</code> is specified, any other regions
* specified will not be painted, for example
--- a/jdk/src/java.desktop/share/classes/sun/swing/text/TextComponentPrintable.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/swing/text/TextComponentPrintable.java Thu Jun 04 18:49:37 2015 -0700
@@ -212,7 +212,7 @@
* level {@code JEditorPanes}. For instance if there is a frame
* inside the frame it will return the top frame only.
*
- * @param c the container to find all frames under
+ * @param container the container to find all frames under
* @param list {@code List} to append the results too
*/
private static void getFrames(final Container container, List<JEditorPane> list) {
--- a/jdk/src/java.desktop/unix/classes/sun/awt/FcFontManager.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/FcFontManager.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -98,8 +98,7 @@
return info;
}
- protected native String getFontPathNative(boolean noType1Fonts,
- boolean isX11GE);
+ native String getFontPathNative(boolean noType1Fonts, boolean isX11GE);
protected synchronized String getFontPath(boolean noType1Fonts) {
return getFontPathNative(noType1Fonts, false);
--- a/jdk/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java Thu Jun 04 18:49:37 2015 -0700
@@ -189,7 +189,7 @@
* @param stockId String which defines the stock id of the gtk item.
* For a complete list reference the API at www.gtk.org for StockItems.
* @param iconSize One of the GtkIconSize values defined in GTKConstants
- * @param textDirection One of the TextDirection values defined in
+ * @param direction One of the TextDirection values defined in
* GTKConstants
* @param detail Render detail that is passed to the native engine (feel
* free to pass null)
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java Thu Jun 04 18:49:37 2015 -0700
@@ -338,7 +338,6 @@
/** Gets the window property for the specified window
* @param window window id to use
- * @param str value to set to.
* @return string with the property.
* @since 1.5
*/
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XComponentPeer.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XComponentPeer.java Thu Jun 04 18:49:37 2015 -0700
@@ -680,7 +680,6 @@
* obtained
* @return the font metrics for <code>font</code>
* @see #getFont
- * @see #getPeer
* @see java.awt.peer.ComponentPeer#getFontMetrics(Font)
* @see Toolkit#getFontMetrics(Font)
* @since 1.0
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XListPeer.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XListPeer.java Thu Jun 04 18:49:37 2015 -0700
@@ -1952,7 +1952,6 @@
* Paint the horizontal scrollbar to the screen
*
* @param g the graphics context to draw into
- * @param colors the colors used to draw the scrollbar
* @param paintAll paint the whole scrollbar if true, just the thumb if false
*/
void paintHorScrollbar(Graphics g, boolean paintAll) {
@@ -1964,7 +1963,6 @@
* Paint the vertical scrollbar to the screen
*
* @param g the graphics context to draw into
- * @param colors the colors used to draw the scrollbar
* @param paintAll paint the whole scrollbar if true, just the thumb if false
*/
void paintVerScrollbar(Graphics g, boolean paintAll) {
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuBarPeer.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuBarPeer.java Thu Jun 04 18:49:37 2015 -0700
@@ -222,7 +222,7 @@
}
/**
- * @see XBaseMenuWindow.map
+ * @see XBaseMenuWindow#map
*/
protected MappingData map() {
XMenuItemPeer[] itemVector = copyItems();
@@ -292,7 +292,7 @@
}
/**
- * @see XBaseMenuWindow.getSubmenuBounds
+ * @see XBaseMenuWindow#getSubmenuBounds
*/
protected Rectangle getSubmenuBounds(Rectangle itemBounds, Dimension windowSize) {
Rectangle globalBounds = toGlobal(itemBounds);
@@ -362,7 +362,7 @@
************************************************/
/**
- * @see XBaseMenuWindow.doDispose()
+ * @see XBaseMenuWindow#doDispose()
*/
protected void doDispose() {
super.doDispose();
@@ -388,7 +388,7 @@
/**
* Performs ungrabbing of input
- * @see XBaseWindow.ungrabInputImpl()
+ * @see XBaseWindow#ungrabInputImpl()
*/
void ungrabInputImpl() {
selectItem(null, false);
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuItemPeer.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuItemPeer.java Thu Jun 04 18:49:37 2015 -0700
@@ -437,7 +437,7 @@
* Sets mapping of item to window.
* @param bounds bounds of item in container's coordinates
* @param textOrigin point for drawString in container's coordinates
- * @see XBaseMenuWindow.map()
+ * @see XBaseMenuWindow#map()
*/
void map(Rectangle bounds, Point textOrigin) {
this.bounds = bounds;
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuWindow.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuWindow.java Thu Jun 04 18:49:37 2015 -0700
@@ -192,14 +192,14 @@
************************************************/
/**
- * @see XBaseMenuWindow.getParentMenuWindow()
+ * @see XBaseMenuWindow#getParentMenuWindow()
*/
protected XBaseMenuWindow getParentMenuWindow() {
return (menuPeer != null) ? menuPeer.getContainer() : null;
}
/**
- * @see XBaseMenuWindow.map()
+ * @see XBaseMenuWindow#map()
*/
protected MappingData map() {
//TODO:Implement popup-menu caption mapping and painting and tear-off
@@ -274,7 +274,7 @@
}
/**
- * @see XBaseMenuWindow.getSubmenuBounds()
+ * @see XBaseMenuWindow#getSubmenuBounds
*/
protected Rectangle getSubmenuBounds(Rectangle itemBounds, Dimension windowSize) {
Rectangle globalBounds = toGlobal(itemBounds);
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XScrollbar.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XScrollbar.java Thu Jun 04 18:49:37 2015 -0700
@@ -161,8 +161,6 @@
* paint the scrollbar
* @param g the graphics context to paint into
* @param colors the colors to use when painting the scrollbar
- * @param width the width of the scrollbar
- * @param height the height of the scrollbar
* @param paintAll paint the whole scrollbar if true, just the thumb is false
*/
void paint(Graphics g, Color colors[], boolean paintAll) {
@@ -393,7 +391,7 @@
/**
* Scroll one unit.
- * @see notifyValue
+ * @see #notifyValue
*/
void scroll() {
switch (mode) {
@@ -607,7 +605,6 @@
* @param minimum is the minimum value of the scrollbar
* @param maximum is the maximum value of the scrollbar
* @param unitSize is the unit size for increment or decrement of the value
- * @param page is the block size for increment or decrement of the value
* @see #setValues
*/
synchronized void setValues(int value, int visible, int minimum, int maximum,
@@ -631,7 +628,7 @@
/**
* Sets the value of this Scrollbar to the specified value.
- * @param value the new value of the Scrollbar. If this value is
+ * @param newValue the new value of the Scrollbar. If this value is
* below the current minimum or above the current maximum minus
* the visible amount, it becomes the new one of those values,
* respectively.
@@ -655,7 +652,7 @@
/**
* Sets the minimum value for this Scrollbar.
- * @param minimum the minimum value of the scrollbar
+ * @param newMinimum the minimum value of the scrollbar
*/
synchronized void setMinimum(int newMinimum) {
/* Use setValues so that a consistent policy
@@ -675,7 +672,7 @@
/**
* Sets the maximum value for this Scrollbar.
- * @param maximum the maximum value of the scrollbar
+ * @param newMaximum the maximum value of the scrollbar
*/
synchronized void setMaximum(int newMaximum) {
/* Use setValues so that a consistent policy
@@ -694,7 +691,7 @@
/**
* Sets the visible amount of this Scrollbar, which is the range
* of values represented by the width of the scroll bar's bubble.
- * @param visible the amount visible per page
+ * @param newAmount the amount visible per page
*/
synchronized void setVisibleAmount(int newAmount) {
setValues(val, newAmount, min, max);
@@ -759,7 +756,7 @@
/**
* Returns the scale factor for the thumbArea ( thumbAreaH / (max - min)).
- * @see #getArrowAreaSize
+ * @see #getArrowAreaWidth
*/
private double getScaleFactor(){
double f = (double)(barLength - 2*getArrowAreaWidth()) / Math.max(1,(max - min));
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java Thu Jun 04 18:49:37 2015 -0700
@@ -616,14 +616,19 @@
}
}
}
- if( keyEventLog.isLoggable(PlatformLogger.Level.FINE) && (ev.get_type() == XConstants.KeyPress || ev.get_type() == XConstants.KeyRelease) ) {
- keyEventLog.fine("before XFilterEvent:"+ev);
+ if (keyEventLog.isLoggable(PlatformLogger.Level.FINE) && (
+ ev.get_type() == XConstants.KeyPress
+ || ev.get_type() == XConstants.KeyRelease)) {
+ keyEventLog.fine("before XFilterEvent:" + ev);
}
if (XlibWrapper.XFilterEvent(ev.getPData(), w)) {
continue;
}
- if( keyEventLog.isLoggable(PlatformLogger.Level.FINE) && (ev.get_type() == XConstants.KeyPress || ev.get_type() == XConstants.KeyRelease) ) {
- keyEventLog.fine("after XFilterEvent:"+ev); // IS THIS CORRECT?
+ if (keyEventLog.isLoggable(PlatformLogger.Level.FINE) && (
+ ev.get_type() == XConstants.KeyPress
+ || ev.get_type() == XConstants.KeyRelease)) {
+ keyEventLog.fine(
+ "after XFilterEvent:" + ev); // IS THIS CORRECT?
}
dispatchEvent(ev);
@@ -639,21 +644,28 @@
}
}
+ /**
+ * Listener installed to detect display changes.
+ */
+ private static final DisplayChangedListener displayChangedHandler =
+ new DisplayChangedListener() {
+ @Override
+ public void displayChanged() {
+ // 7045370: Reset the cached values
+ XToolkit.screenWidth = -1;
+ XToolkit.screenHeight = -1;
+ }
+
+ @Override
+ public void paletteChanged() {
+ }
+ };
+
static {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
if (ge instanceof SunGraphicsEnvironment) {
- ((SunGraphicsEnvironment)ge).addDisplayChangedListener(
- new DisplayChangedListener() {
- @Override
- public void displayChanged() {
- // 7045370: Reset the cached values
- XToolkit.screenWidth = -1;
- XToolkit.screenHeight = -1;
- }
-
- @Override
- public void paletteChanged() {}
- });
+ ((SunGraphicsEnvironment) ge).addDisplayChangedListener(
+ displayChangedHandler);
}
}
@@ -663,7 +675,9 @@
try {
XWindowAttributes pattr = new XWindowAttributes();
try {
- XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(), pattr.pData);
+ XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
+ XToolkit.getDefaultRootWindow(),
+ pattr.pData);
screenWidth = pattr.get_width();
screenHeight = pattr.get_height();
} finally {
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11FontManager.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11FontManager.java Thu Jun 04 18:49:37 2015 -0700
@@ -44,7 +44,6 @@
import sun.font.CompositeFont;
import sun.font.FontManager;
import sun.font.SunFontManager;
-import sun.font.FontConfigManager;
import sun.font.FcFontConfiguration;
import sun.font.FontAccess;
import sun.font.FontUtilities;
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsConfig.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsConfig.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,6 @@
import java.awt.AWTException;
import java.awt.BufferCapabilities;
-import java.awt.BufferCapabilities.FlipContents;
import java.awt.Component;
import java.awt.Toolkit;
import java.awt.GraphicsConfiguration;
@@ -35,7 +34,6 @@
import java.awt.Image;
import java.awt.ImageCapabilities;
import java.awt.Transparency;
-import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.color.ColorSpace;
import java.awt.image.ComponentColorModel;
@@ -55,13 +53,12 @@
import sun.awt.image.OffScreenImage;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
-import sun.awt.X11ComponentPeer;
/**
* This is an implementation of a GraphicsConfiguration object for a
* single X11 visual.
*
- * @see GraphicsEnvironment
+ * @see java.awt.GraphicsEnvironment
* @see GraphicsDevice
*/
public class X11GraphicsConfig extends GraphicsConfiguration
@@ -314,7 +311,7 @@
return pGetBounds(screen.getScreen());
}
- public native Rectangle pGetBounds(int screenNum);
+ private native Rectangle pGetBounds(int screenNum);
private static class XDBECapabilities extends BufferCapabilities {
public XDBECapabilities() {
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java Thu Jun 04 18:49:37 2015 -0700
@@ -52,10 +52,8 @@
* @see GraphicsEnvironment
* @see GraphicsConfiguration
*/
-public class X11GraphicsDevice
- extends GraphicsDevice
- implements DisplayChangedListener
-{
+public final class X11GraphicsDevice extends GraphicsDevice
+ implements DisplayChangedListener {
int screen;
HashMap<SurfaceType, Object> x11ProxyKeyMap = new HashMap<>();
@@ -201,16 +199,15 @@
/*
* Returns the depth for the given index of graphics configurations.
*/
- public native int getConfigDepth (int index, int screen);
+ private native int getConfigDepth(int index, int screen);
/*
* Returns the colormap for the given index of graphics configurations.
*/
- public native int getConfigColormap (int index, int screen);
-
+ private native int getConfigColormap(int index, int screen);
// Whether or not double-buffering extension is supported
- public static native boolean isDBESupported();
+ static native boolean isDBESupported();
// Callback for adding a new double buffer visual into our set
private void addDoubleBufferVisual(int visNum) {
doubleBufferVisuals.add(Integer.valueOf(visNum));
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsEnvironment.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsEnvironment.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,13 +29,6 @@
import java.awt.GraphicsDevice;
import java.awt.Point;
import java.awt.Rectangle;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.StreamTokenizer;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
@@ -43,11 +36,6 @@
import java.util.*;
-import sun.font.MFontConfiguration;
-import sun.font.FcFontConfiguration;
-import sun.font.Font2D;
-import sun.font.FontManager;
-import sun.font.NativeFont;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.SurfaceManagerFactory;
import sun.java2d.UnixSurfaceManagerFactory;
@@ -60,11 +48,10 @@
* for X11 environments.
*
* @see GraphicsDevice
- * @see GraphicsConfiguration
+ * @see java.awt.GraphicsConfiguration
*/
-public class X11GraphicsEnvironment
- extends SunGraphicsEnvironment
-{
+public final class X11GraphicsEnvironment extends SunGraphicsEnvironment {
+
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11GraphicsEnvironment");
private static final PlatformLogger screenLog = PlatformLogger.getLogger("sun.awt.screen.X11GraphicsEnvironment");
@@ -200,7 +187,7 @@
return new X11GraphicsDevice(screennum);
}
- protected native int getDefaultScreenNum();
+ private native int getDefaultScreenNum();
/**
* Returns the default screen graphics device.
*/
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11InputMethod.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11InputMethod.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -35,16 +35,9 @@
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Window;
-import java.awt.im.InputContext;
import java.awt.im.InputMethodHighlight;
import java.awt.im.spi.InputMethodContext;
import sun.awt.im.InputMethodAdapter;
-import java.awt.event.InputEvent;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseEvent;
-import java.awt.event.FocusEvent;
-import java.awt.event.ComponentEvent;
-import java.awt.event.WindowEvent;
import java.awt.event.InputMethodEvent;
import java.awt.font.TextAttribute;
import java.awt.font.TextHitInfo;
@@ -555,7 +548,7 @@
* method is invoked from the event handler in canvas.c in the
* AWT Toolkit thread context and thus inside the AWT Lock.
* @param str committed text
- * @param long when
+ * @param when when
*/
// NOTE: This method may be called by privileged threads.
// This functionality is implemented in a package-private method
@@ -1095,7 +1088,7 @@
/*
* Native methods
*/
- protected native String resetXIC();
+ private native String resetXIC();
private native void disposeXIC();
private native boolean setCompositionEnabledNative(boolean enable);
private native boolean isCompositionEnabledNative();
--- a/jdk/src/java.desktop/unix/classes/sun/font/FontConfigManager.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/classes/sun/font/FontConfigManager.java Thu Jun 04 18:49:37 2015 -0700
@@ -436,12 +436,6 @@
return (fcInfo.compFont = new CompositeFont(physFont, jdkFont));
}
- /**
- *
- * @param locale
- * @param fcFamily
- * @return
- */
public FcCompFont[] getFontConfigFonts() {
return fontConfigFonts;
}
--- a/jdk/src/java.desktop/unix/native/common/awt/utility/rect.h Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/native/common/awt/utility/rect.h Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
--- a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -904,7 +904,6 @@
static Bool
createXIC(JNIEnv * env, X11InputMethodData *pX11IMData, Window w)
{
- XIC active_ic, passive_ic;
XVaNestedList preedit = NULL;
XVaNestedList status = NULL;
XIMStyle on_the_spot_styles = XIMPreeditCallbacks,
@@ -974,6 +973,12 @@
}
if (active_styles == on_the_spot_styles) {
+ pX11IMData->ic_passive = XCreateIC(X11im,
+ XNClientWindow, w,
+ XNFocusWindow, w,
+ XNInputStyle, passive_styles,
+ NULL);
+
callbacks = (XIMCallback *)malloc(sizeof(XIMCallback) * NCALLBACKS);
if (callbacks == (XIMCallback *)NULL)
return False;
@@ -1024,12 +1029,6 @@
NULL);
XFree((void *)preedit);
#endif /* __linux__ || MACOSX */
- pX11IMData->ic_passive = XCreateIC(X11im,
- XNClientWindow, w,
- XNFocusWindow, w,
- XNInputStyle, passive_styles,
- NULL);
-
} else {
pX11IMData->ic_active = XCreateIC(X11im,
XNClientWindow, w,
--- a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c Thu Jun 04 18:49:37 2015 -0700
@@ -1313,9 +1313,6 @@
{
result = gtk2_widgets[_GTK_COMBO_BOX_TEXT_FIELD_TYPE] =
(*fp_gtk_entry_new)();
-
- GtkSettings* settings = fp_gtk_widget_get_settings(result);
- fp_g_object_set(settings, "gtk-cursor-blink", FALSE, NULL);
}
result = gtk2_widgets[_GTK_COMBO_BOX_TEXT_FIELD_TYPE];
break;
@@ -1360,10 +1357,6 @@
{
gtk2_widgets[_GTK_ENTRY_TYPE] =
(*fp_gtk_entry_new)();
-
- GtkSettings* settings =
- fp_gtk_widget_get_settings(gtk2_widgets[_GTK_ENTRY_TYPE]);
- fp_g_object_set(settings, "gtk-cursor-blink", FALSE, NULL);
}
result = gtk2_widgets[_GTK_ENTRY_TYPE];
break;
@@ -1555,9 +1548,6 @@
{
result = gtk2_widgets[_GTK_SPIN_BUTTON_TYPE] =
(*fp_gtk_spin_button_new)(NULL, 0, 0);
-
- GtkSettings* settings = fp_gtk_widget_get_settings(result);
- fp_g_object_set(settings, "gtk-cursor-blink", FALSE, NULL);
}
result = gtk2_widgets[_GTK_SPIN_BUTTON_TYPE];
break;
@@ -2507,14 +2497,20 @@
return result;
}
-/*
+
jobject get_integer_property(JNIEnv *env, GtkSettings* settings, const gchar* key)
{
- gint intval = NULL;
-
+ gint intval = NULL;
(*fp_g_object_get)(settings, key, &intval, NULL);
return create_Integer(env, intval);
-}*/
+}
+
+jobject get_boolean_property(JNIEnv *env, GtkSettings* settings, const gchar* key)
+{
+ gint intval = NULL;
+ (*fp_g_object_get)(settings, key, &intval, NULL);
+ return create_Boolean(env, intval);
+}
jobject gtk2_get_setting(JNIEnv *env, Setting property)
{
@@ -2526,6 +2522,10 @@
return get_string_property(env, settings, "gtk-font-name");
case GTK_ICON_SIZES:
return get_string_property(env, settings, "gtk-icon-sizes");
+ case GTK_CURSOR_BLINK:
+ return get_boolean_property(env, settings, "gtk-cursor-blink");
+ case GTK_CURSOR_BLINK_TIME:
+ return get_integer_property(env, settings, "gtk-cursor-blink-time");
}
return NULL;
--- a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -140,7 +140,9 @@
typedef enum _Setting
{
GTK_FONT_NAME,
- GTK_ICON_SIZES
+ GTK_ICON_SIZES,
+ GTK_CURSOR_BLINK,
+ GTK_CURSOR_BLINK_TIME
} Setting;
/* GTK types, here to eliminate need for GTK headers at compile time */
--- a/jdk/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -279,9 +279,9 @@
});
}
- protected static native void registerFontWithPlatform(String fontName);
+ private static native void registerFontWithPlatform(String fontName);
- protected static native void deRegisterFontWithPlatform(String fontName);
+ private static native void deRegisterFontWithPlatform(String fontName);
/**
* populate the map with the most common windows fonts.
--- a/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java Thu Jun 04 18:49:37 2015 -0700
@@ -226,7 +226,7 @@
* are disabled. Do not call this function with an index of 0.
* @param index a PixelFormat index
*/
- protected native boolean isPixFmtSupported(int index, int screen);
+ private native boolean isPixFmtSupported(int index, int screen);
/**
* Returns the PixelFormatID of the default graphics configuration
--- a/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,19 +28,11 @@
import java.awt.AWTError;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
-import java.awt.GraphicsEnvironment;
-import java.awt.Toolkit;
import java.awt.peer.ComponentPeer;
-import java.io.File;
-import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.ListIterator;
-import java.util.NoSuchElementException;
-import java.util.StringTokenizer;
-import sun.awt.DisplayChangedListener;
-import sun.awt.SunDisplayChanger;
-import sun.awt.windows.WPrinterJob;
+
import sun.awt.windows.WToolkit;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.SurfaceManagerFactory;
@@ -57,9 +49,8 @@
* @see GraphicsConfiguration
*/
-public class Win32GraphicsEnvironment
- extends SunGraphicsEnvironment
-{
+public final class Win32GraphicsEnvironment extends SunGraphicsEnvironment {
+
static {
// Ensure awt is loaded already. Also, this forces static init
// of WToolkit and Toolkit, which we depend upon
@@ -91,7 +82,7 @@
}
protected native int getNumScreens();
- protected native int getDefaultScreen();
+ private native int getDefaultScreen();
public GraphicsDevice getDefaultScreenDevice() {
GraphicsDevice[] screens = getScreenDevices();
--- a/jdk/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -163,6 +163,27 @@
}
}
+ // Known Folder data
+ static class KnownFolderDefinition {
+ String guid;
+ int category;
+ String name;
+ String description;
+ String parent;
+ String relativePath;
+ String parsingName;
+ String tooltip;
+ String localizedName;
+ String icon;
+ String security;
+ long attributes;
+ int defenitionFlags;
+ String ftidType;
+ String path;
+ String saveLocation;
+ static final List<KnownFolderDefinition> libraries = getLibraries();
+ }
+
static class FolderDisposer implements sun.java2d.DisposerRecord {
/*
* This is cached as a concession to getFolderType(), which needs
@@ -578,7 +599,22 @@
return s;
}
}
- return getDisplayNameOf(parentIShellFolder, relativePIDL, SHGDN_FORPARSING);
+ String path = getDisplayNameOf(parentIShellFolder, relativePIDL,
+ SHGDN_FORPARSING);
+ // if this is a library its default save location is taken as a path
+ // this is a temp fix until java.io starts support Libraries
+ if( path != null && path.startsWith("::{") &&
+ path.toLowerCase().endsWith(".library-ms")) {
+ for (KnownFolderDefinition kf : KnownFolderDefinition.libraries) {
+ if( path.toLowerCase().endsWith(
+ kf.relativePath.toLowerCase()) &&
+ path.toUpperCase().startsWith(
+ kf.parsingName.substring(0, 40).toUpperCase()) ) {
+ return kf.saveLocation;
+ }
+ }
+ }
+ return path;
}
// Needs to be accessible to Win32ShellFolderManager2
@@ -848,6 +884,9 @@
long relativePIDL,
int attrs);
+ // Returns data of all Known Folders registered in the system
+ private static native KnownFolderDefinition[] loadKnownFolders();
+
/**
* @return The name used to display this shell folder
*/
@@ -1178,4 +1217,26 @@
return result == null ? 0 : result;
}
}
+
+ // Extracts libraries and their default save locations from Known Folders list
+ private static List<KnownFolderDefinition> getLibraries() {
+ return invoke(new Callable<List<KnownFolderDefinition>>() {
+ @Override
+ public List<KnownFolderDefinition> call() throws Exception {
+ KnownFolderDefinition[] all = loadKnownFolders();
+ List<KnownFolderDefinition> folders = new ArrayList<>();
+ if (all != null) {
+ for (KnownFolderDefinition kf : all) {
+ if (kf.relativePath == null || kf.parsingName == null ||
+ kf.saveLocation == null) {
+ continue;
+ }
+ folders.add(kf);
+ }
+ }
+ return folders;
+ }
+ });
+ }
+
}
--- a/jdk/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java Thu Jun 04 18:49:37 2015 -0700
@@ -663,7 +663,7 @@
* This method is intentionally not synchronized as it is called while
* holding other locks.
*
- * @see sun.java2d.d3d.D3DScreenUpdateManager#validate(D3DWindowSurfaceData)
+ * @see sun.java2d.d3d.D3DScreenUpdateManager#validate
*/
public Color getBackgroundNoSync() {
return background;
--- a/jdk/src/java.desktop/windows/classes/sun/awt/windows/WObjectPeer.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/classes/sun/awt/windows/WObjectPeer.java Thu Jun 04 18:49:37 2015 -0700
@@ -31,9 +31,9 @@
}
// The Windows handle for the native widget.
- long pData;
+ volatile long pData;
// if the native peer has been destroyed
- boolean destroyed = false;
+ volatile boolean destroyed = false;
// The associated AWT object.
Object target;
--- a/jdk/src/java.desktop/windows/classes/sun/awt/windows/WPathGraphics.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/classes/sun/awt/windows/WPathGraphics.java Thu Jun 04 18:49:37 2015 -0700
@@ -386,7 +386,7 @@
* such as Hebrew and Arabic, the glyphs can be rendered from right to
* left, in which case the coordinate supplied is the location of the
* leftmost character on the baseline.
- * @param s the <code>String</code> to be rendered
+ * @param str the <code>String</code> to be rendered
* @param x, y the coordinates where the <code>String</code>
* should be rendered
* @see #setPaint
@@ -877,7 +877,7 @@
* is transformed by the supplied AffineTransform and
* drawn using GDI to the printer context.
*
- * @param img The image to be drawn.
+ * @param image The image to be drawn.
* @param xform Used to transform the image before drawing.
* This can be null.
* @param bgcolor This color is drawn where the image has transparent
--- a/jdk/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java Thu Jun 04 18:49:37 2015 -0700
@@ -591,7 +591,7 @@
* Throws <code>PrinterException</code> if the specified service
* cannot support the <code>Pageable</code> and
* </code>Printable</code> interfaces necessary to support 2D printing.
- * @param a print service which supports 2D printing.
+ * @param service print service which supports 2D printing.
*
* @throws PrinterException if the specified service does not support
* 2D printing.
--- a/jdk/src/java.desktop/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java Thu Jun 04 18:49:37 2015 -0700
@@ -59,7 +59,7 @@
* GDIWindowSurfaceData. A background thread handles the swap chain flips.
*
* There are some restrictions to which windows we would use this for.
- * @see #createScreenSurface()
+ * @see #createScreenSurface
*/
public class D3DScreenUpdateManager extends ScreenUpdateManager
implements Runnable
@@ -290,7 +290,7 @@
/**
* Adds a surface to the list of tracked surfaces.
*
- * @param d3dw the surface to be added
+ * @param sd the surface to be added
*/
private void trackScreenSurface(SurfaceData sd) {
if (!done && sd instanceof D3DWindowSurfaceData) {
--- a/jdk/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java Thu Jun 04 18:49:37 2015 -0700
@@ -118,7 +118,7 @@
/**
* To be used with getNativeResource() only.
- * @see #getNativeResource()
+ * @see #getNativeResource
*/
public static final int D3D_DEVICE_RESOURCE= 100;
/*
--- a/jdk/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java Thu Jun 04 18:49:37 2015 -0700
@@ -244,7 +244,7 @@
* Updates the layered window with the contents of the surface.
*
* @param psdops pointer to the native ogl sd structure
- * @param pData pointer to the AwtWindow peer data
+ * @param peer pointer to the AwtWindow peer data
* @param w width of the window
* @param h height of the window
* @see sun.awt.windows.TranslucentWindowPainter
--- a/jdk/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -64,6 +64,19 @@
//#include <sun_awt_shell_Win32ShellFolder2.h>
+#ifndef DASSERT
+#define DASSERT(x)
+#endif
+#define DEFINE_FIELD_ID(var, cls, field, type) \
+ jfieldID var = env->GetFieldID(cls, field, type); \
+ DASSERT(var != NULL); \
+ CHECK_NULL_RETURN(var, NULL);
+
+#define EXCEPTION_CHECK \
+ if(env->ExceptionCheck()) { \
+ throw std::bad_alloc(); \
+ }
+
// Shell Functions
typedef BOOL (WINAPI *DestroyIconType)(HICON);
typedef HINSTANCE (WINAPI *FindExecutableType)(LPCTSTR,LPCTSTR,LPTSTR);
@@ -1263,5 +1276,216 @@
return 0;
}
+/*
+ * Class: sun_awt_shell_Win32ShellFolder2
+ * Method: loadKnownFolders
+ * Signature: (V)[BLsun/awt/shell/Win32ShellFolder2$KnownfolderDefenition;
+ */
+JNIEXPORT jobjectArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_loadKnownFolders
+ (JNIEnv* env, jclass cls )
+{
+ CoInitialize(NULL);
+ IKnownFolderManager* pkfm = NULL;
+ HRESULT hr = CoCreateInstance(CLSID_KnownFolderManager, NULL,
+ CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pkfm));
+ if (!SUCCEEDED(hr)) return NULL;
+
+ TRY;
+
+ jclass cl = env->FindClass("sun/awt/shell/Win32ShellFolder2$KnownFolderDefinition");
+ CHECK_NULL_RETURN(cl, NULL);
+ DEFINE_FIELD_ID(field_guid, cl, "guid", "Ljava/lang/String;")
+ DEFINE_FIELD_ID(field_name, cl, "name", "Ljava/lang/String;");
+ DEFINE_FIELD_ID(field_description, cl, "description", "Ljava/lang/String;");
+ DEFINE_FIELD_ID(field_parent, cl, "parent", "Ljava/lang/String;");
+ DEFINE_FIELD_ID(field_relativePath, cl, "relativePath", "Ljava/lang/String;");
+ DEFINE_FIELD_ID(field_parsingName, cl, "parsingName", "Ljava/lang/String;");
+ DEFINE_FIELD_ID(field_tooltip, cl, "tooltip", "Ljava/lang/String;");
+ DEFINE_FIELD_ID(field_localizedName, cl, "localizedName", "Ljava/lang/String;");
+ DEFINE_FIELD_ID(field_icon, cl, "icon", "Ljava/lang/String;");
+ DEFINE_FIELD_ID(field_security, cl, "security", "Ljava/lang/String;");
+ DEFINE_FIELD_ID(field_path, cl, "path", "Ljava/lang/String;");
+ DEFINE_FIELD_ID(field_saveLocation, cl, "saveLocation", "Ljava/lang/String;");
+ DEFINE_FIELD_ID(field_category, cl, "category", "I");
+ DEFINE_FIELD_ID(field_attributes, cl, "attributes", "J");
+ DEFINE_FIELD_ID(field_defenitionFlags, cl, "defenitionFlags", "I");
+ DEFINE_FIELD_ID(field_ftidType, cl, "ftidType", "Ljava/lang/String;");
+
+ jobjectArray result;
+ KNOWNFOLDERID* pFoldersIds = NULL;
+ UINT count = 0;
+ if (SUCCEEDED(pkfm->GetFolderIds(&pFoldersIds, &count))) {
+ jmethodID initMethod;
+ try {
+ result = env->NewObjectArray(count, cl, NULL);
+ initMethod = env->GetMethodID(cl, "<init>", "()V");
+ EXCEPTION_CHECK
+ } catch (std::bad_alloc&) {
+ CoTaskMemFree(pFoldersIds);
+ pkfm->Release();
+ throw;
+ }
+ for(UINT i = 0; i < count; ++i)
+ {
+ jobject fld;
+ const KNOWNFOLDERID& folderId = pFoldersIds[i];
+ LPOLESTR guid = NULL;
+ try {
+ fld = env->NewObject(cl, initMethod);
+ if (fld) {
+ env->SetObjectArrayElement(result, i, fld);
+ }
+ EXCEPTION_CHECK
+
+ if (SUCCEEDED(StringFromCLSID(folderId, &guid))) {
+ jstring jstr = JNU_NewStringPlatform(env, guid);
+ if (jstr) {
+ env->SetObjectField(fld, field_guid, jstr);
+ }
+ CoTaskMemFree(guid);
+ EXCEPTION_CHECK
+ }
+ } catch (std::bad_alloc&) {
+ CoTaskMemFree(pFoldersIds);
+ pkfm->Release();
+ throw;
+ }
+
+ IKnownFolder* pFolder = NULL;
+ if (SUCCEEDED(pkfm->GetFolder(folderId, &pFolder))) {
+ KNOWNFOLDER_DEFINITION kfDef;
+ if (SUCCEEDED(pFolder->GetFolderDefinition(&kfDef)))
+ {
+ try {
+ jstring jstr = JNU_NewStringPlatform(env, kfDef.pszName);
+ if(jstr) {
+ env->SetObjectField(fld, field_name, jstr);
+ }
+ EXCEPTION_CHECK
+ if (kfDef.pszDescription) {
+ jstr = JNU_NewStringPlatform(env, kfDef.pszDescription);
+ if (jstr) {
+ env->SetObjectField(fld, field_description, jstr);
+ }
+ EXCEPTION_CHECK
+ }
+ EXCEPTION_CHECK
+ if (SUCCEEDED(StringFromCLSID(kfDef.fidParent, &guid))) {
+ jstr = JNU_NewStringPlatform(env, guid);
+ if (jstr) {
+ env->SetObjectField(fld, field_parent, jstr);
+ }
+ CoTaskMemFree(guid);
+ EXCEPTION_CHECK
+ }
+ if (kfDef.pszRelativePath) {
+ jstr = JNU_NewStringPlatform(env, kfDef.pszRelativePath);
+ if (jstr) {
+ env->SetObjectField(fld, field_relativePath, jstr);
+ }
+ EXCEPTION_CHECK
+ }
+ if (kfDef.pszParsingName) {
+ jstr = JNU_NewStringPlatform(env, kfDef.pszParsingName);
+ if (jstr) {
+ env->SetObjectField(fld, field_parsingName, jstr);
+ }
+ EXCEPTION_CHECK
+ }
+ if (kfDef.pszTooltip) {
+ jstr = JNU_NewStringPlatform(env, kfDef.pszTooltip);
+ if (jstr) {
+ env->SetObjectField(fld, field_tooltip, jstr);
+ }
+ EXCEPTION_CHECK
+ }
+ if (kfDef.pszLocalizedName) {
+ jstr = JNU_NewStringPlatform(env, kfDef.pszLocalizedName);
+ if (jstr) {
+ env->SetObjectField(fld, field_localizedName, jstr);
+ }
+ EXCEPTION_CHECK
+ }
+ if (kfDef.pszIcon) {
+ jstr = JNU_NewStringPlatform(env, kfDef.pszIcon);
+ if (jstr) {
+ env->SetObjectField(fld, field_icon, jstr);
+ }
+ EXCEPTION_CHECK
+ }
+ if (kfDef.pszSecurity) {
+ jstr = JNU_NewStringPlatform(env, kfDef.pszSecurity);
+ if (jstr) {
+ env->SetObjectField(fld, field_security, jstr);
+ }
+ EXCEPTION_CHECK
+ }
+ if (SUCCEEDED(StringFromCLSID(kfDef.ftidType, &guid))) {
+ jstr = JNU_NewStringPlatform(env, guid);
+ if (jstr) {
+ env->SetObjectField(fld, field_ftidType, jstr);
+ }
+ CoTaskMemFree(guid);
+ EXCEPTION_CHECK
+ }
+ env->SetIntField(fld, field_category, kfDef.category);
+ env->SetIntField(fld, field_defenitionFlags, kfDef.kfdFlags);
+ env->SetLongField(fld, field_attributes, kfDef.dwAttributes);
+
+ LPWSTR folderPath = NULL;
+ if (SUCCEEDED(pFolder->GetPath(KF_FLAG_NO_ALIAS, &folderPath))
+ && folderPath) {
+ jstr = JNU_NewStringPlatform(env, folderPath);
+ if (jstr) {
+ env->SetObjectField(fld, field_path, jstr);
+ }
+ CoTaskMemFree(folderPath);
+ EXCEPTION_CHECK
+ }
+
+ IShellLibrary *plib = NULL;
+ hr = CoCreateInstance(CLSID_ShellLibrary, NULL,
+ CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&plib));
+ if (SUCCEEDED(hr)) {
+ hr = plib->LoadLibraryFromKnownFolder(folderId, STGM_READWRITE);
+ if (SUCCEEDED(hr)) {
+ IShellItem *item = NULL;
+ hr = plib->GetDefaultSaveFolder(DSFT_DETECT,
+ IID_PPV_ARGS(&item));
+ if (SUCCEEDED(hr) && item) {
+ LPWSTR loc = NULL;
+ hr = item->GetDisplayName(SIGDN_FILESYSPATH, &loc);
+ if (SUCCEEDED(hr) && loc)
+ {
+ jstr = JNU_NewStringPlatform(env, loc);
+ if (jstr) {
+ env->SetObjectField(fld, field_saveLocation, jstr);
+ }
+ CoTaskMemFree(loc);
+ }
+ item->Release();
+ }
+ }
+ plib->Release();
+ EXCEPTION_CHECK
+ }
+ FreeKnownFolderDefinitionFields(&kfDef);
+ } catch (std::bad_alloc&) {
+ FreeKnownFolderDefinitionFields(&kfDef);
+ pFolder->Release();
+ CoTaskMemFree(pFoldersIds);
+ pkfm->Release();
+ throw;
+ }
+ }
+ }
+ pFolder->Release();
+ }
+ CoTaskMemFree(pFoldersIds);
+ }
+ pkfm->Release();
+ return result;
+ CATCH_BAD_ALLOC_RET(NULL);
+}
} // extern "C"
--- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp Thu Jun 04 18:49:37 2015 -0700
@@ -1864,6 +1864,7 @@
AwtFrame::activateEmbeddingTopLevelMID = env->GetMethodID(cls, "activateEmbeddingTopLevel", "()V");
DASSERT(AwtFrame::activateEmbeddingTopLevelMID != NULL);
+ CHECK_NULL(AwtFrame::activateEmbeddingTopLevelMID);
AwtFrame::isEmbeddedInIEID = env->GetFieldID(cls, "isEmbeddedInIE", "Z");
DASSERT(AwtFrame::isEmbeddedInIEID != NULL);
--- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Menu.cpp Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Menu.cpp Thu Jun 04 18:49:37 2015 -0700
@@ -239,6 +239,7 @@
}
jobject menuItem = env->CallObjectMethod(target, AwtMenu::getItemMID,
index);
+ if (!menuItem) return NULL; // menu item was removed concurrently
DASSERT(!safe_ExceptionOccurred(env));
jobject wMenuItemPeer = GetPeerForTarget(env, menuItem);
@@ -264,9 +265,9 @@
}
/* target is a java.awt.Menu */
jobject target = GetTarget(env);
-
+ if(!target || env->ExceptionCheck()) return;
int nCount = CountItem(target);
- for (int i = 0; i < nCount; i++) {
+ for (int i = 0; i < nCount && !env->ExceptionCheck(); i++) {
AwtMenuItem* awtMenuItem = GetItem(target, i);
if (awtMenuItem != NULL) {
SendDrawItem(awtMenuItem, drawInfo);
@@ -294,8 +295,9 @@
}
/* target is a java.awt.Menu */
jobject target = GetTarget(env);
+ if(!target || env->ExceptionCheck()) return;
int nCount = CountItem(target);
- for (int i = 0; i < nCount; i++) {
+ for (int i = 0; i < nCount && !env->ExceptionCheck(); i++) {
AwtMenuItem* awtMenuItem = GetItem(target, i);
if (awtMenuItem != NULL) {
SendMeasureItem(awtMenuItem, hDC, measureInfo);
--- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_MenuBar.cpp Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_MenuBar.cpp Thu Jun 04 18:49:37 2015 -0700
@@ -156,13 +156,16 @@
}
jobject menu = env->CallObjectMethod(target, AwtMenuBar::getMenuMID,index);
+ if (!menu) return NULL; // menu item was removed concurrently
DASSERT(!safe_ExceptionOccurred(env));
jobject menuItemPeer = GetPeerForTarget(env, menu);
PDATA pData;
- JNI_CHECK_PEER_RETURN_NULL(menuItemPeer);
+ JNI_CHECK_PEER_GOTO(menuItemPeer, done);
+
AwtMenuItem* awtMenuItem = (AwtMenuItem*)pData;
+done:
env->DeleteLocalRef(menu);
env->DeleteLocalRef(menuItemPeer);
--- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp Thu Jun 04 18:49:37 2015 -0700
@@ -112,6 +112,7 @@
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
if (m_peerObject != NULL) {
+ JNI_SET_DESTROYED(m_peerObject);
JNI_SET_PDATA(m_peerObject, NULL);
env->DeleteGlobalRef(m_peerObject);
m_peerObject = NULL;
--- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_new.cpp Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_new.cpp Thu Jun 04 18:49:37 2015 -0700
@@ -172,6 +172,9 @@
env->ExceptionClear();
// rethrow exception
env->Throw(xcp);
+ // temp solution to reveal all concurrency issues in jtreg and JCK
+ // we will switch it back to silent mode before the release
+ env->ExceptionDescribe();
return xcp;
}
}
--- a/jdk/src/java.management/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.management/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,14 +33,12 @@
import com.sun.jmx.remote.util.EnvHelp;
import java.io.InvalidObjectException;
-import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
@@ -1138,55 +1136,13 @@
to getters. */
private static final class CompositeBuilderViaConstructor
extends CompositeBuilder {
- static class AnnotationHelper {
- private static Class<? extends Annotation> constructorPropertiesClass;
- private static Method valueMethod;
- static {
- findConstructorPropertiesClass();
- }
-
- @SuppressWarnings("unchecked")
- private static void findConstructorPropertiesClass() {
- try {
- constructorPropertiesClass = (Class<? extends Annotation>)
- Class.forName("java.beans.ConstructorProperties", false,
- DefaultMXBeanMappingFactory.class.getClassLoader());
- valueMethod = constructorPropertiesClass.getMethod("value");
- } catch (ClassNotFoundException cnf) {
- // java.beans not present
- } catch (NoSuchMethodException e) {
- // should not reach here
- throw new InternalError(e);
- }
- }
-
- static boolean isAvailable() {
- return constructorPropertiesClass != null;
- }
-
- static String[] getPropertyNames(Constructor<?> constr) {
- if (!isAvailable())
- return null;
-
- Annotation a = constr.getAnnotation(constructorPropertiesClass);
- if (a == null) return null;
-
- try {
- return (String[]) valueMethod.invoke(a);
- } catch (InvocationTargetException e) {
- throw new InternalError(e);
- } catch (IllegalAccessException e) {
- throw new InternalError(e);
- }
- }
- }
CompositeBuilderViaConstructor(Class<?> targetClass, String[] itemNames) {
super(targetClass, itemNames);
}
String applicable(Method[] getters) throws InvalidObjectException {
- if (!AnnotationHelper.isAvailable())
+ if (!JavaBeansAccessor.isAvailable())
return "@ConstructorProperties annotation not available";
Class<?> targetClass = getTargetClass();
@@ -1196,7 +1152,7 @@
List<Constructor<?>> annotatedConstrList = newList();
for (Constructor<?> constr : constrs) {
if (Modifier.isPublic(constr.getModifiers())
- && AnnotationHelper.getPropertyNames(constr) != null)
+ && JavaBeansAccessor.getConstructorPropertiesValue(constr) != null)
annotatedConstrList.add(constr);
}
@@ -1225,7 +1181,7 @@
// so we can test unambiguity.
Set<BitSet> getterIndexSets = newSet();
for (Constructor<?> constr : annotatedConstrList) {
- String[] propertyNames = AnnotationHelper.getPropertyNames(constr);
+ String[] propertyNames = JavaBeansAccessor.getConstructorPropertiesValue(constr);
Type[] paramTypes = constr.getGenericParameterTypes();
if (paramTypes.length != propertyNames.length) {
--- a/jdk/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -56,7 +56,7 @@
import javax.management.AttributeNotFoundException;
import javax.management.openmbean.CompositeData;
-import sun.misc.JavaBeansIntrospectorAccess;
+import sun.misc.JavaBeansAccess;
import sun.misc.SharedSecrets;
import sun.reflect.misc.MethodUtil;
import sun.reflect.misc.ReflectUtil;
@@ -552,10 +552,8 @@
// Java Beans introspection
//
Class<?> clazz = complex.getClass();
- Method readMethod;
- if (BeansIntrospector.isAvailable()) {
- readMethod = BeansIntrospector.getReadMethod(clazz, element);
- } else {
+ Method readMethod = JavaBeansAccessor.getReadMethod(clazz, element);
+ if (readMethod == null) {
// Java Beans not available so use simple introspection
// to locate method
readMethod = SimpleIntrospector.getReadMethod(clazz, element);
@@ -580,30 +578,6 @@
}
/**
- * Provides access to java.beans.Introspector if available.
- */
- private static class BeansIntrospector {
- private static final JavaBeansIntrospectorAccess JBIA;
- static {
- // ensure that java.beans.Introspector is initialized (if present)
- try {
- Class.forName("java.beans.Introspector", true,
- BeansIntrospector.class.getClassLoader());
- } catch (ClassNotFoundException ignore) { }
-
- JBIA = SharedSecrets.getJavaBeansIntrospectorAccess();
- }
-
- static boolean isAvailable() {
- return JBIA != null;
- }
-
- static Method getReadMethod(Class<?> clazz, String property) throws Exception {
- return JBIA.getReadMethod(clazz, property);
- }
- }
-
- /**
* A simple introspector that uses reflection to analyze a class and
* identify its "getter" methods. This class is intended for use only when
* Java Beans is not present (which implies that there isn't explicit
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.management/share/classes/com/sun/jmx/mbeanserver/JavaBeansAccessor.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.jmx.mbeanserver;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import sun.misc.JavaBeansAccess;
+import sun.misc.SharedSecrets;
+
+/**
+ * A centralized place for gaining access to java.beans related functionality -
+ * if available.
+ */
+class JavaBeansAccessor {
+ static {
+ // ensure that java.beans.Introspector is initialized (if present)
+ // it will fill in the SharedSecrets
+ try {
+ Class.forName("java.beans.Introspector", true,
+ JavaBeansAccessor.class.getClassLoader());
+ } catch (ClassNotFoundException ignore) { }
+ }
+
+ private static JavaBeansAccess getJavaBeansAccess() {
+ return SharedSecrets.getJavaBeansAccess();
+ }
+
+ static boolean isAvailable() {
+ return getJavaBeansAccess() != null;
+ }
+
+ /**
+ * Returns the getter method for a property of the given name
+ * @param clazz The JavaBeans class
+ * @param property The property name
+ * @return The resolved property getter name or null
+ * @throws Exception
+ */
+ static Method getReadMethod(Class<?> clazz, String property) throws Exception {
+ JavaBeansAccess jba = getJavaBeansAccess();
+ return jba != null ? jba.getReadMethod(clazz, property) : null;
+ }
+
+ /**
+ * Return the <b>value</b> attribute of the associated
+ * <code>@ConstructorProperties</code> annotation if that is present.
+ * @param ctr The constructor to extract the annotation value from
+ * @return The {@code value} attribute of the <code>@ConstructorProperties</code>
+ * annotation or {@code null} if the constructor is not annotated by
+ * this annotation or the annotation is not accessible.
+ */
+ static String[] getConstructorPropertiesValue(Constructor<?> ctr) {
+ JavaBeansAccess jba = getJavaBeansAccess();
+ return jba != null ? jba.getConstructorPropertiesValue(ctr) : null;
+ }
+}
--- a/jdk/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java Thu Jun 04 18:49:37 2015 -0700
@@ -393,9 +393,14 @@
BerDecoder readReply(LdapRequest ldr)
throws IOException, NamingException {
BerDecoder rber;
- boolean waited = false;
- while (((rber = ldr.getReplyBer()) == null) && !waited) {
+ // Track down elapsed time to workaround spurious wakeups
+ long elapsedMilli = 0;
+ long elapsedNano = 0;
+
+ while (((rber = ldr.getReplyBer()) == null) &&
+ (readTimeout <= 0 || elapsedMilli < readTimeout))
+ {
try {
// If socket closed, don't even try
synchronized (this) {
@@ -409,11 +414,15 @@
rber = ldr.getReplyBer();
if (rber == null) {
if (readTimeout > 0) { // Socket read timeout is specified
+ long beginNano = System.nanoTime();
- // will be woken up before readTimeout only if reply is
+ // will be woken up before readTimeout if reply is
// available
- ldr.wait(readTimeout);
- waited = true;
+ ldr.wait(readTimeout - elapsedMilli);
+ elapsedNano += (System.nanoTime() - beginNano);
+ elapsedMilli += elapsedNano / 1000_000;
+ elapsedNano %= 1000_000;
+
} else {
// no timeout is set so we wait infinitely until
// a response is received
@@ -430,7 +439,7 @@
}
}
- if ((rber == null) && waited) {
+ if ((rber == null) && (elapsedMilli >= readTimeout)) {
abandonRequest(ldr, null);
throw new NamingException("LDAP response read timed out, timeout used:"
+ readTimeout + "ms." );
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.security.jgss/share/classes/META-INF/services/sun.security.ssl.ClientKeyExchangeService Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,2 @@
+sun.security.krb5.internal.ssl.Krb5KeyExchangeService
+
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/Config.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/Config.java Thu Jun 04 18:49:37 2015 -0700
@@ -260,7 +260,11 @@
}
/**
- * Gets all values for the specified keys.
+ * Gets all values (at least one) for the specified keys separated by
+ * a whitespace, or null if there is no such keys.
+ * The values can either be provided on a single line, or on multiple lines
+ * using the same key. When provided on a single line, the value can be
+ * comma or space separated.
* @throws IllegalArgumentException if any of the keys is illegal
* (See {@link #get})
*/
@@ -270,6 +274,7 @@
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String s: v) {
+ s = s.replaceAll("[\\s,]+", " ");
if (first) {
sb.append(s);
first = false;
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/KrbCred.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/KrbCred.java Thu Jun 04 18:49:37 2015 -0700
@@ -34,6 +34,9 @@
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.KeyUsage;
import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
import sun.security.util.DerValue;
/**
@@ -76,10 +79,24 @@
options.set(KDCOptions.FORWARDABLE, true);
HostAddresses sAddrs = null;
- // XXX Also NT_GSS_KRB5_PRINCIPAL can be a host based principal
+
// GSSName.NT_HOSTBASED_SERVICE should display with KRB_NT_SRV_HST
- if (server.getNameType() == PrincipalName.KRB_NT_SRV_HST)
- sAddrs= new HostAddresses(server);
+ if (server.getNameType() == PrincipalName.KRB_NT_SRV_HST) {
+ sAddrs = new HostAddresses(server);
+ } else if (server.getNameType() == PrincipalName.KRB_NT_UNKNOWN) {
+ // Sometimes this is also a server
+ if (server.getNameStrings().length >= 2) {
+ String host = server.getNameStrings()[1];
+ try {
+ InetAddress[] addr = InetAddress.getAllByName(host);
+ if (addr != null && addr.length > 0) {
+ sAddrs = new HostAddresses(addr);
+ }
+ } catch (UnknownHostException ioe) {
+ // maybe we guessed wrong, let sAddrs be null
+ }
+ }
+ }
KrbTgsReq tgsReq = new KrbTgsReq(options, tgt, tgService,
null, null, null, null, sAddrs, null, null, null);
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/HostAddresses.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/HostAddresses.java Thu Jun 04 18:49:37 2015 -0700
@@ -31,16 +31,14 @@
package sun.security.krb5.internal;
+import sun.security.krb5.Config;
import sun.security.krb5.PrincipalName;
import sun.security.krb5.KrbException;
import sun.security.krb5.Asn1Exception;
import sun.security.util.*;
-import java.util.Vector;
-import java.util.ArrayList;
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.UnknownHostException;
+
+import java.net.*;
+import java.util.*;
import java.io.IOException;
import sun.security.krb5.internal.ccache.CCacheOutputStream;
@@ -293,34 +291,35 @@
*/
public static HostAddresses getLocalAddresses() throws IOException
{
- String hostname = null;
- InetAddress[] inetAddresses = null;
+ Set<InetAddress> all = new LinkedHashSet<>();
try {
- InetAddress localHost = InetAddress.getLocalHost();
- hostname = localHost.getHostName();
- inetAddresses = InetAddress.getAllByName(hostname);
- HostAddress[] hAddresses = new HostAddress[inetAddresses.length];
- for (int i = 0; i < inetAddresses.length; i++)
- {
- hAddresses[i] = new HostAddress(inetAddresses[i]);
- }
if (DEBUG) {
- System.out.println(">>> KrbKdcReq local addresses for "
- + hostname + " are: ");
-
- for (int i = 0; i < inetAddresses.length; i++) {
- System.out.println("\n\t" + inetAddresses[i]);
- if (inetAddresses[i] instanceof Inet4Address)
- System.out.println("IPv4 address");
- if (inetAddresses[i] instanceof Inet6Address)
- System.out.println("IPv6 address");
+ System.out.println(">>> KrbKdcReq local addresses are:");
+ }
+ String extra = Config.getInstance().getAll(
+ "libdefaults", "extra_addresses");
+ if (extra != null) {
+ for (String s: extra.split("\\s+")) {
+ all.add(InetAddress.getByName(s));
+ if (DEBUG) {
+ System.out.println(" extra_addresses: "
+ + InetAddress.getByName(s));
+ }
}
}
- return (new HostAddresses(hAddresses));
+ for (NetworkInterface ni:
+ Collections.list(NetworkInterface.getNetworkInterfaces())) {
+ if (DEBUG) {
+ System.out.println(" NetworkInterface " + ni + ":");
+ System.out.println(" "
+ + Collections.list(ni.getInetAddresses()));
+ }
+ all.addAll(Collections.list(ni.getInetAddresses()));
+ }
+ return new HostAddresses(all.toArray(new InetAddress[all.size()]));
} catch (Exception exc) {
throw new IOException(exc.toString());
}
-
}
/**
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/KerberosPreMasterSecret.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.krb5.internal.ssl;
+
+import java.io.*;
+import java.security.*;
+import java.util.Arrays;
+
+import javax.net.ssl.*;
+
+import sun.security.krb5.EncryptionKey;
+import sun.security.krb5.EncryptedData;
+import sun.security.krb5.KrbException;
+import sun.security.krb5.internal.crypto.KeyUsage;
+
+import sun.security.ssl.Debug;
+import sun.security.ssl.HandshakeInStream;
+import sun.security.ssl.HandshakeMessage;
+import sun.security.ssl.ProtocolVersion;
+
+/**
+ * This is the Kerberos premaster secret in the Kerberos client key
+ * exchange message (CLIENT --> SERVER); it holds the
+ * Kerberos-encrypted pre-master secret. The secret is encrypted using the
+ * Kerberos session key. The padding and size of the resulting message
+ * depends on the session key type, but the pre-master secret is
+ * always exactly 48 bytes.
+ *
+ */
+final class KerberosPreMasterSecret {
+
+ private ProtocolVersion protocolVersion; // preMaster [0,1]
+ private byte preMaster[]; // 48 bytes
+ private byte encrypted[];
+
+ /**
+ * Constructor used by client to generate premaster secret.
+ *
+ * Client randomly creates a pre-master secret and encrypts it
+ * using the Kerberos session key; only the server can decrypt
+ * it, using the session key available in the service ticket.
+ *
+ * @param protocolVersion used to set preMaster[0,1]
+ * @param generator random number generator for generating premaster secret
+ * @param sessionKey Kerberos session key for encrypting premaster secret
+ */
+ KerberosPreMasterSecret(ProtocolVersion protocolVersion,
+ SecureRandom generator, EncryptionKey sessionKey) throws IOException {
+
+ if (sessionKey.getEType() ==
+ EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
+ throw new IOException(
+ "session keys with des3-cbc-hmac-sha1-kd encryption type " +
+ "are not supported for TLS Kerberos cipher suites");
+ }
+
+ this.protocolVersion = protocolVersion;
+ preMaster = generatePreMaster(generator, protocolVersion);
+
+ // Encrypt premaster secret
+ try {
+ EncryptedData eData = new EncryptedData(sessionKey, preMaster,
+ KeyUsage.KU_UNKNOWN);
+ encrypted = eData.getBytes(); // not ASN.1 encoded.
+
+ } catch (KrbException e) {
+ throw (SSLKeyException)new SSLKeyException
+ ("Kerberos premaster secret error").initCause(e);
+ }
+ }
+
+ /*
+ * Constructor used by server to decrypt encrypted premaster secret.
+ * The protocol version in preMaster[0,1] must match either currentVersion
+ * or clientVersion, otherwise, the premaster secret is set to
+ * a random one to foil possible attack.
+ *
+ * @param currentVersion version of protocol being used
+ * @param clientVersion version requested by client
+ * @param generator random number generator used to generate
+ * bogus premaster secret if premaster secret verification fails
+ * @param input input stream from which to read the encrypted
+ * premaster secret
+ * @param sessionKey Kerberos session key to be used for decryption
+ */
+ KerberosPreMasterSecret(ProtocolVersion currentVersion,
+ ProtocolVersion clientVersion,
+ SecureRandom generator, byte[] encrypted,
+ EncryptionKey sessionKey) throws IOException {
+
+ if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
+ if (encrypted != null) {
+ Debug.println(System.out,
+ "encrypted premaster secret", encrypted);
+ }
+ }
+
+ if (sessionKey.getEType() ==
+ EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
+ throw new IOException(
+ "session keys with des3-cbc-hmac-sha1-kd encryption type " +
+ "are not supported for TLS Kerberos cipher suites");
+ }
+
+ // Decrypt premaster secret
+ try {
+ EncryptedData data = new EncryptedData(sessionKey.getEType(),
+ null /* optional kvno */, encrypted);
+
+ byte[] temp = data.decrypt(sessionKey, KeyUsage.KU_UNKNOWN);
+ if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
+ if (encrypted != null) {
+ Debug.println(System.out,
+ "decrypted premaster secret", temp);
+ }
+ }
+
+ // Remove padding bytes after decryption. Only DES and DES3 have
+ // paddings and we don't support DES3 in TLS (see above)
+
+ if (temp.length == 52 &&
+ data.getEType() == EncryptedData.ETYPE_DES_CBC_CRC) {
+ // For des-cbc-crc, 4 paddings. Value can be 0x04 or 0x00.
+ if (paddingByteIs(temp, 52, (byte)4) ||
+ paddingByteIs(temp, 52, (byte)0)) {
+ temp = Arrays.copyOf(temp, 48);
+ }
+ } else if (temp.length == 56 &&
+ data.getEType() == EncryptedData.ETYPE_DES_CBC_MD5) {
+ // For des-cbc-md5, 8 paddings with 0x08, or no padding
+ if (paddingByteIs(temp, 56, (byte)8)) {
+ temp = Arrays.copyOf(temp, 48);
+ }
+ }
+
+ preMaster = temp;
+
+ protocolVersion = ProtocolVersion.valueOf(preMaster[0],
+ preMaster[1]);
+ if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
+ System.out.println("Kerberos PreMasterSecret version: "
+ + protocolVersion);
+ }
+ } catch (Exception e) {
+ // catch exception & process below
+ preMaster = null;
+ protocolVersion = currentVersion;
+ }
+
+ // check if the premaster secret version is ok
+ // the specification says that it must be the maximum version supported
+ // by the client from its ClientHello message. However, many
+ // old implementations send the negotiated version, so accept both
+ // for SSL v3.0 and TLS v1.0.
+ // NOTE that we may be comparing two unsupported version numbers in
+ // the second case, which is why we cannot use object references
+ // equality in this special case
+ boolean versionMismatch = (protocolVersion.v != clientVersion.v);
+
+ /*
+ * we never checked the client_version in server side
+ * for TLS v1.0 and SSL v3.0. For compatibility, we
+ * maintain this behavior.
+ */
+ if (versionMismatch && (clientVersion.v <= 0x0301)) {
+ versionMismatch = (protocolVersion.v != currentVersion.v);
+ }
+
+ /*
+ * Bogus decrypted ClientKeyExchange? If so, conjure a
+ * a random preMaster secret that will fail later during
+ * Finished message processing. This is a countermeasure against
+ * the "interactive RSA PKCS#1 encryption envelop attack" reported
+ * in June 1998. Preserving the executation path will
+ * mitigate timing attacks and force consistent error handling
+ * that will prevent an attacking client from differentiating
+ * different kinds of decrypted ClientKeyExchange bogosities.
+ */
+ if ((preMaster == null) || (preMaster.length != 48)
+ || versionMismatch) {
+ if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
+ System.out.println("Kerberos PreMasterSecret error, "
+ + "generating random secret");
+ if (preMaster != null) {
+ Debug.println(System.out, "Invalid secret", preMaster);
+ }
+ }
+
+ /*
+ * Randomize the preMaster secret with the
+ * ClientHello.client_version, as will produce invalid master
+ * secret to prevent the attacks.
+ */
+ preMaster = generatePreMaster(generator, clientVersion);
+ protocolVersion = clientVersion;
+ }
+ }
+
+ /**
+ * Checks if all paddings of data are b
+ * @param data the block with padding
+ * @param len length of data, >= 48
+ * @param b expected padding byte
+ */
+ private static boolean paddingByteIs(byte[] data, int len, byte b) {
+ for (int i=48; i<len; i++) {
+ if (data[i] != b) return false;
+ }
+ return true;
+ }
+
+ /*
+ * Used by server to generate premaster secret in case of
+ * problem decoding ticket.
+ *
+ * @param protocolVersion used for preMaster[0,1]
+ * @param generator random number generator to use for generating secret.
+ */
+ KerberosPreMasterSecret(ProtocolVersion protocolVersion,
+ SecureRandom generator) {
+
+ this.protocolVersion = protocolVersion;
+ preMaster = generatePreMaster(generator, protocolVersion);
+ }
+
+ private static byte[] generatePreMaster(SecureRandom rand,
+ ProtocolVersion ver) {
+
+ byte[] pm = new byte[48];
+ rand.nextBytes(pm);
+ pm[0] = ver.major;
+ pm[1] = ver.minor;
+
+ return pm;
+ }
+
+ // Clone not needed; internal use only
+ byte[] getUnencrypted() {
+ return preMaster;
+ }
+
+ // Clone not needed; internal use only
+ byte[] getEncrypted() {
+ return encrypted;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/Krb5KeyExchangeService.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.krb5.internal.ssl;
+
+import sun.security.ssl.ClientKeyExchange;
+import sun.security.ssl.Debug;
+import sun.security.ssl.ClientKeyExchangeService;
+import sun.security.ssl.HandshakeOutStream;
+
+import sun.security.jgss.GSSCaller;
+import sun.security.jgss.krb5.Krb5Util;
+import sun.security.jgss.krb5.ServiceCreds;
+import sun.security.krb5.EncryptedData;
+import sun.security.krb5.EncryptionKey;
+import sun.security.krb5.KrbException;
+import sun.security.krb5.PrincipalName;
+import sun.security.krb5.internal.EncTicketPart;
+import sun.security.krb5.internal.Ticket;
+import sun.security.krb5.internal.crypto.KeyUsage;
+import sun.security.ssl.ProtocolVersion;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.KerberosTicket;
+import javax.security.auth.kerberos.KeyTab;
+import javax.security.auth.kerberos.ServicePermission;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.InetAddress;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.SecureRandom;
+import java.util.Set;
+
+/**
+ * The provider for TLS_KRB_ cipher suites.
+ *
+ * @since 1.9
+ */
+public class Krb5KeyExchangeService implements ClientKeyExchangeService {
+
+ public static final Debug debug = Debug.getInstance("ssl");
+
+ @Override
+ public String[] supported() {
+ return new String[] { "KRB5", "KRB5_EXPORT" };
+ }
+
+ @Override
+ public Object getServiceCreds(AccessControlContext acc) {
+ try {
+ ServiceCreds serviceCreds = AccessController.doPrivileged(
+ (PrivilegedExceptionAction<ServiceCreds>)
+ () -> Krb5Util.getServiceCreds(
+ GSSCaller.CALLER_SSL_SERVER, null, acc));
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Using Kerberos creds");
+ }
+ String serverPrincipal = serviceCreds.getName();
+ if (serverPrincipal != null) {
+ // When service is bound, we check ASAP. Otherwise,
+ // will check after client request is received
+ // in in Kerberos ClientKeyExchange
+ SecurityManager sm = System.getSecurityManager();
+ try {
+ if (sm != null) {
+ // Eliminate dependency on ServicePermission
+ sm.checkPermission(new ServicePermission(
+ serverPrincipal, "accept"), acc);
+ }
+ } catch (SecurityException se) {
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Permission to access Kerberos"
+ + " secret key denied");
+ }
+ return null;
+ }
+ }
+ return serviceCreds;
+ } catch (PrivilegedActionException e) {
+ // Likely exception here is LoginException
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Attempt to obtain Kerberos key failed: "
+ + e.toString());
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public String getServiceHostName(Principal principal) {
+ if (principal == null) {
+ return null;
+ }
+ String hostName = null;
+ try {
+ PrincipalName princName =
+ new PrincipalName(principal.getName(),
+ PrincipalName.KRB_NT_SRV_HST);
+ String[] nameParts = princName.getNameStrings();
+ if (nameParts.length >= 2) {
+ hostName = nameParts[1];
+ }
+ } catch (Exception e) {
+ // ignore
+ }
+ return hostName;
+ }
+
+
+ @Override
+ public boolean isRelated(boolean isClient,
+ AccessControlContext acc, Principal p) {
+
+ if (p == null) return false;
+ try {
+ Subject subject = AccessController.doPrivileged(
+ (PrivilegedExceptionAction<Subject>)
+ () -> Krb5Util.getSubject(
+ isClient ? GSSCaller.CALLER_SSL_CLIENT
+ : GSSCaller.CALLER_SSL_SERVER,
+ acc));
+ if (subject == null) {
+ if (debug != null && Debug.isOn("session")) {
+ System.out.println("Kerberos credentials are" +
+ " not present in the current Subject;" +
+ " check if " +
+ " javax.security.auth.useSubjectAsCreds" +
+ " system property has been set to false");
+ }
+ return false;
+ }
+ Set<Principal> principals =
+ subject.getPrincipals(Principal.class);
+ if (principals.contains(p)) {
+ // bound to this principal
+ return true;
+ } else {
+ if (isClient) {
+ return false;
+ } else {
+ for (KeyTab pc : subject.getPrivateCredentials(KeyTab.class)) {
+ if (!pc.isBound()) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ } catch (PrivilegedActionException pae) {
+ if (debug != null && Debug.isOn("session")) {
+ System.out.println("Attempt to obtain" +
+ " subject failed! " + pae);
+ }
+ return false;
+ }
+
+ }
+
+ public ClientKeyExchange createClientExchange(
+ String serverName, AccessControlContext acc,
+ ProtocolVersion protocolVerson, SecureRandom rand) throws IOException {
+ return new ExchangerImpl(serverName, acc, protocolVerson, rand);
+ }
+
+ public ClientKeyExchange createServerExchange(
+ ProtocolVersion protocolVersion, ProtocolVersion clientVersion,
+ SecureRandom rand, byte[] encodedTicket, byte[] encrypted,
+ AccessControlContext acc, Object serviceCreds) throws IOException {
+ return new ExchangerImpl(protocolVersion, clientVersion, rand,
+ encodedTicket, encrypted, acc, serviceCreds);
+ }
+
+ static class ExchangerImpl extends ClientKeyExchange {
+
+ final private KerberosPreMasterSecret preMaster;
+ final private byte[] encodedTicket;
+ final private KerberosPrincipal peerPrincipal;
+ final private KerberosPrincipal localPrincipal;
+
+ @Override
+ public int messageLength() {
+ return encodedTicket.length + preMaster.getEncrypted().length + 6;
+ }
+
+ @Override
+ public void send(HandshakeOutStream s) throws IOException {
+ s.putBytes16(encodedTicket);
+ s.putBytes16(null);
+ s.putBytes16(preMaster.getEncrypted());
+ }
+
+ @Override
+ public void print(PrintStream s) throws IOException {
+ s.println("*** ClientKeyExchange, Kerberos");
+
+ if (debug != null && Debug.isOn("verbose")) {
+ Debug.println(s, "Kerberos service ticket", encodedTicket);
+ Debug.println(s, "Random Secret", preMaster.getUnencrypted());
+ Debug.println(s, "Encrypted random Secret", preMaster.getEncrypted());
+ }
+ }
+
+ ExchangerImpl(String serverName, AccessControlContext acc,
+ ProtocolVersion protocolVersion, SecureRandom rand) throws IOException {
+
+ // Get service ticket
+ KerberosTicket ticket = getServiceTicket(serverName, acc);
+ encodedTicket = ticket.getEncoded();
+
+ // Record the Kerberos principals
+ peerPrincipal = ticket.getServer();
+ localPrincipal = ticket.getClient();
+
+ // Optional authenticator, encrypted using session key,
+ // currently ignored
+
+ // Generate premaster secret and encrypt it using session key
+ EncryptionKey sessionKey = new EncryptionKey(
+ ticket.getSessionKeyType(),
+ ticket.getSessionKey().getEncoded());
+
+ preMaster = new KerberosPreMasterSecret(protocolVersion,
+ rand, sessionKey);
+ }
+
+ ExchangerImpl(
+ ProtocolVersion protocolVersion, ProtocolVersion clientVersion, SecureRandom rand,
+ byte[] encodedTicket, byte[] encrypted,
+ AccessControlContext acc, Object serviceCreds) throws IOException {
+
+ // Read ticket
+ this.encodedTicket = encodedTicket;
+
+ if (debug != null && Debug.isOn("verbose")) {
+ Debug.println(System.out,
+ "encoded Kerberos service ticket", encodedTicket);
+ }
+
+ EncryptionKey sessionKey = null;
+ KerberosPrincipal tmpPeer = null;
+ KerberosPrincipal tmpLocal = null;
+
+ try {
+ Ticket t = new Ticket(encodedTicket);
+
+ EncryptedData encPart = t.encPart;
+ PrincipalName ticketSname = t.sname;
+
+ final ServiceCreds creds = (ServiceCreds)serviceCreds;
+ final KerberosPrincipal princ =
+ new KerberosPrincipal(ticketSname.toString());
+
+ // For bound service, permission already checked at setup
+ if (creds.getName() == null) {
+ SecurityManager sm = System.getSecurityManager();
+ try {
+ if (sm != null) {
+ // Eliminate dependency on ServicePermission
+ sm.checkPermission(new ServicePermission(
+ ticketSname.toString(), "accept"), acc);
+ }
+ } catch (SecurityException se) {
+ serviceCreds = null;
+ // Do not destroy keys. Will affect Subject
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Permission to access Kerberos"
+ + " secret key denied");
+ se.printStackTrace(System.out);
+ }
+ throw new IOException("Kerberos service not allowedy");
+ }
+ }
+ KerberosKey[] serverKeys = AccessController.doPrivileged(
+ new PrivilegedAction<KerberosKey[]>() {
+ @Override
+ public KerberosKey[] run() {
+ return creds.getKKeys(princ);
+ }
+ });
+ if (serverKeys.length == 0) {
+ throw new IOException("Found no key for " + princ +
+ (creds.getName() == null ? "" :
+ (", this keytab is for " + creds.getName() + " only")));
+ }
+
+ /*
+ * permission to access and use the secret key of the Kerberized
+ * "host" service is done in ServerHandshaker.getKerberosKeys()
+ * to ensure server has the permission to use the secret key
+ * before promising the client
+ */
+
+ // See if we have the right key to decrypt the ticket to get
+ // the session key.
+ int encPartKeyType = encPart.getEType();
+ Integer encPartKeyVersion = encPart.getKeyVersionNumber();
+ KerberosKey dkey = null;
+ try {
+ dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys);
+ } catch (KrbException ke) { // a kvno mismatch
+ throw new IOException(
+ "Cannot find key matching version number", ke);
+ }
+ if (dkey == null) {
+ // %%% Should print string repr of etype
+ throw new IOException("Cannot find key of appropriate type" +
+ " to decrypt ticket - need etype " + encPartKeyType);
+ }
+
+ EncryptionKey secretKey = new EncryptionKey(
+ encPartKeyType,
+ dkey.getEncoded());
+
+ // Decrypt encPart using server's secret key
+ byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET);
+
+ // Reset data stream after decryption, remove redundant bytes
+ byte[] temp = encPart.reset(bytes);
+ EncTicketPart encTicketPart = new EncTicketPart(temp);
+
+ // Record the Kerberos Principals
+ tmpPeer = new KerberosPrincipal(encTicketPart.cname.getName());
+ tmpLocal = new KerberosPrincipal(ticketSname.getName());
+
+ sessionKey = encTicketPart.key;
+
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("server principal: " + ticketSname);
+ System.out.println("cname: " + encTicketPart.cname.toString());
+ }
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("KerberosWrapper error getting session key,"
+ + " generating random secret (" + e.getMessage() + ")");
+ }
+ sessionKey = null;
+ }
+
+ //input.getBytes16(); // XXX Read and ignore authenticator
+
+ if (sessionKey != null) {
+ preMaster = new KerberosPreMasterSecret(protocolVersion,
+ clientVersion, rand, encrypted, sessionKey);
+ } else {
+ // Generate bogus premaster secret
+ preMaster = new KerberosPreMasterSecret(clientVersion, rand);
+ }
+
+ peerPrincipal = tmpPeer;
+ localPrincipal = tmpLocal;
+ }
+
+ // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context
+ private static KerberosTicket getServiceTicket(String serverName,
+ final AccessControlContext acc) throws IOException {
+
+ if ("localhost".equals(serverName) ||
+ "localhost.localdomain".equals(serverName)) {
+
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Get the local hostname");
+ }
+ String localHost = java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<String>() {
+ public String run() {
+ try {
+ return InetAddress.getLocalHost().getHostName();
+ } catch (java.net.UnknownHostException e) {
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Warning,"
+ + " cannot get the local hostname: "
+ + e.getMessage());
+ }
+ return null;
+ }
+ }
+ });
+ if (localHost != null) {
+ serverName = localHost;
+ }
+ }
+
+ // Resolve serverName (possibly in IP addr form) to Kerberos principal
+ // name for service with hostname
+ String serviceName = "host/" + serverName;
+ PrincipalName principal;
+ try {
+ principal = new PrincipalName(serviceName,
+ PrincipalName.KRB_NT_SRV_HST);
+ } catch (SecurityException se) {
+ throw se;
+ } catch (Exception e) {
+ IOException ioe = new IOException("Invalid service principal" +
+ " name: " + serviceName);
+ ioe.initCause(e);
+ throw ioe;
+ }
+ String realm = principal.getRealmAsString();
+
+ final String serverPrincipal = principal.toString();
+ final String tgsPrincipal = "krbtgt/" + realm + "@" + realm;
+ final String clientPrincipal = null; // use default
+
+
+ // check permission to obtain a service ticket to initiate a
+ // context with the "host" service
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new ServicePermission(serverPrincipal,
+ "initiate"), acc);
+ }
+
+ try {
+ KerberosTicket ticket = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<KerberosTicket>() {
+ public KerberosTicket run() throws Exception {
+ return Krb5Util.getTicketFromSubjectAndTgs(
+ GSSCaller.CALLER_SSL_CLIENT,
+ clientPrincipal, serverPrincipal,
+ tgsPrincipal, acc);
+ }});
+
+ if (ticket == null) {
+ throw new IOException("Failed to find any kerberos service" +
+ " ticket for " + serverPrincipal);
+ }
+ return ticket;
+ } catch (PrivilegedActionException e) {
+ IOException ioe = new IOException(
+ "Attempt to obtain kerberos service ticket for " +
+ serverPrincipal + " failed!");
+ ioe.initCause(e);
+ throw ioe;
+ }
+ }
+
+ @Override
+ public SecretKey clientKeyExchange() {
+ byte[] secretBytes = preMaster.getUnencrypted();
+ return new SecretKeySpec(secretBytes, "TlsPremasterSecret");
+ }
+
+ @Override
+ public Principal getPeerPrincipal() {
+ return peerPrincipal;
+ }
+
+ @Override
+ public Principal getLocalPrincipal() {
+ return localPrincipal;
+ }
+
+ /**
+ * Determines if a kvno matches another kvno. Used in the method
+ * findKey(etype, version, keys). Always returns true if either input
+ * is null or zero, in case any side does not have kvno info available.
+ *
+ * Note: zero is included because N/A is not a legal value for kvno
+ * in javax.security.auth.kerberos.KerberosKey. Therefore, the info
+ * that the kvno is N/A might be lost when converting between
+ * EncryptionKey and KerberosKey.
+ */
+ private static boolean versionMatches(Integer v1, int v2) {
+ if (v1 == null || v1 == 0 || v2 == 0) {
+ return true;
+ }
+ return v1.equals(v2);
+ }
+
+ private static KerberosKey findKey(int etype, Integer version,
+ KerberosKey[] keys) throws KrbException {
+ int ktype;
+ boolean etypeFound = false;
+
+ // When no matched kvno is found, returns tke key of the same
+ // etype with the highest kvno
+ int kvno_found = 0;
+ KerberosKey key_found = null;
+
+ for (int i = 0; i < keys.length; i++) {
+ ktype = keys[i].getKeyType();
+ if (etype == ktype) {
+ int kv = keys[i].getVersionNumber();
+ etypeFound = true;
+ if (versionMatches(version, kv)) {
+ return keys[i];
+ } else if (kv > kvno_found) {
+ key_found = keys[i];
+ kvno_found = kv;
+ }
+ }
+ }
+ // Key not found.
+ // %%% kludge to allow DES keys to be used for diff etypes
+ if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
+ etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
+ for (int i = 0; i < keys.length; i++) {
+ ktype = keys[i].getKeyType();
+ if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
+ ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
+ int kv = keys[i].getVersionNumber();
+ etypeFound = true;
+ if (versionMatches(version, kv)) {
+ return new KerberosKey(keys[i].getPrincipal(),
+ keys[i].getEncoded(),
+ etype,
+ kv);
+ } else if (kv > kvno_found) {
+ key_found = new KerberosKey(keys[i].getPrincipal(),
+ keys[i].getEncoded(),
+ etype,
+ kv);
+ kvno_found = kv;
+ }
+ }
+ }
+ }
+ if (etypeFound) {
+ return key_found;
+ }
+ return null;
+ }
+ }
+}
--- a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,463 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl.krb5;
-
-import java.io.IOException;
-import java.io.PrintStream;
-import java.security.AccessController;
-import java.security.AccessControlContext;
-import java.security.PrivilegedExceptionAction;
-import java.security.PrivilegedActionException;
-import java.security.SecureRandom;
-import java.net.InetAddress;
-import java.security.PrivilegedAction;
-
-import javax.security.auth.kerberos.KerberosTicket;
-import javax.security.auth.kerberos.KerberosKey;
-import javax.security.auth.kerberos.KerberosPrincipal;
-import javax.security.auth.kerberos.ServicePermission;
-import sun.security.jgss.GSSCaller;
-
-import sun.security.krb5.EncryptionKey;
-import sun.security.krb5.EncryptedData;
-import sun.security.krb5.PrincipalName;
-import sun.security.krb5.internal.Ticket;
-import sun.security.krb5.internal.EncTicketPart;
-import sun.security.krb5.internal.crypto.KeyUsage;
-
-import sun.security.jgss.krb5.Krb5Util;
-import sun.security.jgss.krb5.ServiceCreds;
-import sun.security.krb5.KrbException;
-import sun.security.krb5.internal.Krb5;
-
-import sun.security.ssl.Debug;
-import sun.security.ssl.HandshakeInStream;
-import sun.security.ssl.HandshakeOutStream;
-import sun.security.ssl.Krb5Helper;
-import sun.security.ssl.ProtocolVersion;
-
-/**
- * This is Kerberos option in the client key exchange message
- * (CLIENT -> SERVER). It holds the Kerberos ticket and the encrypted
- * premaster secret encrypted with the session key sealed in the ticket.
- * From RFC 2712:
- * struct
- * {
- * opaque Ticket;
- * opaque authenticator; // optional
- * opaque EncryptedPreMasterSecret; // encrypted with the session key
- * // which is sealed in the ticket
- * } KerberosWrapper;
- *
- *
- * Ticket and authenticator are encrypted as per RFC 1510 (in ASN.1)
- * Encrypted pre-master secret has the same structure as it does for RSA
- * except for Kerberos, the encryption key is the session key instead of
- * the RSA public key.
- *
- * XXX authenticator currently ignored
- *
- */
-public final class KerberosClientKeyExchangeImpl
- extends sun.security.ssl.KerberosClientKeyExchange {
-
- private KerberosPreMasterSecret preMaster;
- private byte[] encodedTicket;
- private KerberosPrincipal peerPrincipal;
- private KerberosPrincipal localPrincipal;
-
- public KerberosClientKeyExchangeImpl() {
- }
-
- /**
- * Creates an instance of KerberosClientKeyExchange consisting of the
- * Kerberos service ticket, authenticator and encrypted premaster secret.
- * Called by client handshaker.
- *
- * @param serverName name of server with which to do handshake;
- * this is used to get the Kerberos service ticket
- * @param protocolVersion Maximum version supported by client (i.e,
- * version it requested in client hello)
- * @param rand random number generator to use for generating pre-master
- * secret
- */
- @Override
- public void init(String serverName,
- AccessControlContext acc, ProtocolVersion protocolVersion,
- SecureRandom rand) throws IOException {
-
- // Get service ticket
- KerberosTicket ticket = getServiceTicket(serverName, acc);
- encodedTicket = ticket.getEncoded();
-
- // Record the Kerberos principals
- peerPrincipal = ticket.getServer();
- localPrincipal = ticket.getClient();
-
- // Optional authenticator, encrypted using session key,
- // currently ignored
-
- // Generate premaster secret and encrypt it using session key
- EncryptionKey sessionKey = new EncryptionKey(
- ticket.getSessionKeyType(),
- ticket.getSessionKey().getEncoded());
-
- preMaster = new KerberosPreMasterSecret(protocolVersion,
- rand, sessionKey);
- }
-
- /**
- * Creates an instance of KerberosClientKeyExchange from its ASN.1 encoding.
- * Used by ServerHandshaker to verify and obtain premaster secret.
- *
- * @param protocolVersion current protocol version
- * @param clientVersion version requested by client in its ClientHello;
- * used by premaster secret version check
- * @param rand random number generator used for generating random
- * premaster secret if ticket and/or premaster verification fails
- * @param input inputstream from which to get ASN.1-encoded KerberosWrapper
- * @param acc the AccessControlContext of the handshaker
- * @param serviceCreds server's creds
- */
- @Override
- public void init(ProtocolVersion protocolVersion,
- ProtocolVersion clientVersion,
- SecureRandom rand, HandshakeInStream input, AccessControlContext acc, Object serviceCreds)
- throws IOException {
-
- // Read ticket
- encodedTicket = input.getBytes16();
-
- if (debug != null && Debug.isOn("verbose")) {
- Debug.println(System.out,
- "encoded Kerberos service ticket", encodedTicket);
- }
-
- EncryptionKey sessionKey = null;
-
- try {
- Ticket t = new Ticket(encodedTicket);
-
- EncryptedData encPart = t.encPart;
- PrincipalName ticketSname = t.sname;
-
- final ServiceCreds creds = (ServiceCreds)serviceCreds;
- final KerberosPrincipal princ =
- new KerberosPrincipal(ticketSname.toString());
-
- // For bound service, permission already checked at setup
- if (creds.getName() == null) {
- SecurityManager sm = System.getSecurityManager();
- try {
- if (sm != null) {
- // Eliminate dependency on ServicePermission
- sm.checkPermission(Krb5Helper.getServicePermission(
- ticketSname.toString(), "accept"), acc);
- }
- } catch (SecurityException se) {
- serviceCreds = null;
- // Do not destroy keys. Will affect Subject
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Permission to access Kerberos"
- + " secret key denied");
- }
- throw new IOException("Kerberos service not allowedy");
- }
- }
- KerberosKey[] serverKeys = AccessController.doPrivileged(
- new PrivilegedAction<KerberosKey[]>() {
- @Override
- public KerberosKey[] run() {
- return creds.getKKeys(princ);
- }
- });
- if (serverKeys.length == 0) {
- throw new IOException("Found no key for " + princ +
- (creds.getName() == null ? "" :
- (", this keytab is for " + creds.getName() + " only")));
- }
-
- /*
- * permission to access and use the secret key of the Kerberized
- * "host" service is done in ServerHandshaker.getKerberosKeys()
- * to ensure server has the permission to use the secret key
- * before promising the client
- */
-
- // See if we have the right key to decrypt the ticket to get
- // the session key.
- int encPartKeyType = encPart.getEType();
- Integer encPartKeyVersion = encPart.getKeyVersionNumber();
- KerberosKey dkey = null;
- try {
- dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys);
- } catch (KrbException ke) { // a kvno mismatch
- throw new IOException(
- "Cannot find key matching version number", ke);
- }
- if (dkey == null) {
- // %%% Should print string repr of etype
- throw new IOException("Cannot find key of appropriate type" +
- " to decrypt ticket - need etype " + encPartKeyType);
- }
-
- EncryptionKey secretKey = new EncryptionKey(
- encPartKeyType,
- dkey.getEncoded());
-
- // Decrypt encPart using server's secret key
- byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET);
-
- // Reset data stream after decryption, remove redundant bytes
- byte[] temp = encPart.reset(bytes);
- EncTicketPart encTicketPart = new EncTicketPart(temp);
-
- // Record the Kerberos Principals
- peerPrincipal =
- new KerberosPrincipal(encTicketPart.cname.getName());
- localPrincipal = new KerberosPrincipal(ticketSname.getName());
-
- sessionKey = encTicketPart.key;
-
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("server principal: " + ticketSname);
- System.out.println("cname: " + encTicketPart.cname.toString());
- }
- } catch (IOException e) {
- throw e;
- } catch (Exception e) {
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("KerberosWrapper error getting session key,"
- + " generating random secret (" + e.getMessage() + ")");
- }
- sessionKey = null;
- }
-
- input.getBytes16(); // XXX Read and ignore authenticator
-
- if (sessionKey != null) {
- preMaster = new KerberosPreMasterSecret(protocolVersion,
- clientVersion, rand, input, sessionKey);
- } else {
- // Generate bogus premaster secret
- preMaster = new KerberosPreMasterSecret(clientVersion, rand);
- }
- }
-
- @Override
- public int messageLength() {
- return (6 + encodedTicket.length + preMaster.getEncrypted().length);
- }
-
- @Override
- public void send(HandshakeOutStream s) throws IOException {
- s.putBytes16(encodedTicket);
- s.putBytes16(null); // XXX no authenticator
- s.putBytes16(preMaster.getEncrypted());
- }
-
- @Override
- public void print(PrintStream s) throws IOException {
- s.println("*** ClientKeyExchange, Kerberos");
-
- if (debug != null && Debug.isOn("verbose")) {
- Debug.println(s, "Kerberos service ticket", encodedTicket);
- Debug.println(s, "Random Secret", preMaster.getUnencrypted());
- Debug.println(s, "Encrypted random Secret",
- preMaster.getEncrypted());
- }
- }
-
- // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context
- private static KerberosTicket getServiceTicket(String serverName,
- final AccessControlContext acc) throws IOException {
-
- if ("localhost".equals(serverName) ||
- "localhost.localdomain".equals(serverName)) {
-
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Get the local hostname");
- }
- String localHost = java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction<String>() {
- public String run() {
- try {
- return InetAddress.getLocalHost().getHostName();
- } catch (java.net.UnknownHostException e) {
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Warning,"
- + " cannot get the local hostname: "
- + e.getMessage());
- }
- return null;
- }
- }
- });
- if (localHost != null) {
- serverName = localHost;
- }
- }
-
- // Resolve serverName (possibly in IP addr form) to Kerberos principal
- // name for service with hostname
- String serviceName = "host/" + serverName;
- PrincipalName principal;
- try {
- principal = new PrincipalName(serviceName,
- PrincipalName.KRB_NT_SRV_HST);
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- IOException ioe = new IOException("Invalid service principal" +
- " name: " + serviceName);
- ioe.initCause(e);
- throw ioe;
- }
- String realm = principal.getRealmAsString();
-
- final String serverPrincipal = principal.toString();
- final String tgsPrincipal = "krbtgt/" + realm + "@" + realm;
- final String clientPrincipal = null; // use default
-
-
- // check permission to obtain a service ticket to initiate a
- // context with the "host" service
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- sm.checkPermission(new ServicePermission(serverPrincipal,
- "initiate"), acc);
- }
-
- try {
- KerberosTicket ticket = AccessController.doPrivileged(
- new PrivilegedExceptionAction<KerberosTicket>() {
- public KerberosTicket run() throws Exception {
- return Krb5Util.getTicketFromSubjectAndTgs(
- GSSCaller.CALLER_SSL_CLIENT,
- clientPrincipal, serverPrincipal,
- tgsPrincipal, acc);
- }});
-
- if (ticket == null) {
- throw new IOException("Failed to find any kerberos service" +
- " ticket for " + serverPrincipal);
- }
- return ticket;
- } catch (PrivilegedActionException e) {
- IOException ioe = new IOException(
- "Attempt to obtain kerberos service ticket for " +
- serverPrincipal + " failed!");
- ioe.initCause(e);
- throw ioe;
- }
- }
-
- @Override
- public byte[] getUnencryptedPreMasterSecret() {
- return preMaster.getUnencrypted();
- }
-
- @Override
- public KerberosPrincipal getPeerPrincipal() {
- return peerPrincipal;
- }
-
- @Override
- public KerberosPrincipal getLocalPrincipal() {
- return localPrincipal;
- }
-
- /**
- * Determines if a kvno matches another kvno. Used in the method
- * findKey(etype, version, keys). Always returns true if either input
- * is null or zero, in case any side does not have kvno info available.
- *
- * Note: zero is included because N/A is not a legal value for kvno
- * in javax.security.auth.kerberos.KerberosKey. Therefore, the info
- * that the kvno is N/A might be lost when converting between
- * EncryptionKey and KerberosKey.
- */
- private static boolean versionMatches(Integer v1, int v2) {
- if (v1 == null || v1 == 0 || v2 == 0) {
- return true;
- }
- return v1.equals(v2);
- }
-
- private static KerberosKey findKey(int etype, Integer version,
- KerberosKey[] keys) throws KrbException {
- int ktype;
- boolean etypeFound = false;
-
- // When no matched kvno is found, returns tke key of the same
- // etype with the highest kvno
- int kvno_found = 0;
- KerberosKey key_found = null;
-
- for (int i = 0; i < keys.length; i++) {
- ktype = keys[i].getKeyType();
- if (etype == ktype) {
- int kv = keys[i].getVersionNumber();
- etypeFound = true;
- if (versionMatches(version, kv)) {
- return keys[i];
- } else if (kv > kvno_found) {
- key_found = keys[i];
- kvno_found = kv;
- }
- }
- }
- // Key not found.
- // %%% kludge to allow DES keys to be used for diff etypes
- if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
- etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
- for (int i = 0; i < keys.length; i++) {
- ktype = keys[i].getKeyType();
- if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
- ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
- int kv = keys[i].getVersionNumber();
- etypeFound = true;
- if (versionMatches(version, kv)) {
- return new KerberosKey(keys[i].getPrincipal(),
- keys[i].getEncoded(),
- etype,
- kv);
- } else if (kv > kvno_found) {
- key_found = new KerberosKey(keys[i].getPrincipal(),
- keys[i].getEncoded(),
- etype,
- kv);
- kvno_found = kv;
- }
- }
- }
- }
- if (etypeFound) {
- return key_found;
- }
- return null;
- }
-}
--- a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosPreMasterSecret.java Thu Jun 04 09:31:49 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,272 +0,0 @@
-/*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl.krb5;
-
-import java.io.*;
-import java.security.*;
-import java.util.Arrays;
-
-import javax.net.ssl.*;
-
-import sun.security.krb5.EncryptionKey;
-import sun.security.krb5.EncryptedData;
-import sun.security.krb5.KrbException;
-import sun.security.krb5.internal.crypto.KeyUsage;
-
-import sun.security.ssl.Debug;
-import sun.security.ssl.HandshakeInStream;
-import sun.security.ssl.HandshakeMessage;
-import sun.security.ssl.ProtocolVersion;
-
-/**
- * This is the Kerberos premaster secret in the Kerberos client key
- * exchange message (CLIENT --> SERVER); it holds the
- * Kerberos-encrypted pre-master secret. The secret is encrypted using the
- * Kerberos session key. The padding and size of the resulting message
- * depends on the session key type, but the pre-master secret is
- * always exactly 48 bytes.
- *
- */
-final class KerberosPreMasterSecret {
-
- private ProtocolVersion protocolVersion; // preMaster [0,1]
- private byte preMaster[]; // 48 bytes
- private byte encrypted[];
-
- /**
- * Constructor used by client to generate premaster secret.
- *
- * Client randomly creates a pre-master secret and encrypts it
- * using the Kerberos session key; only the server can decrypt
- * it, using the session key available in the service ticket.
- *
- * @param protocolVersion used to set preMaster[0,1]
- * @param generator random number generator for generating premaster secret
- * @param sessionKey Kerberos session key for encrypting premaster secret
- */
- KerberosPreMasterSecret(ProtocolVersion protocolVersion,
- SecureRandom generator, EncryptionKey sessionKey) throws IOException {
-
- if (sessionKey.getEType() ==
- EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
- throw new IOException(
- "session keys with des3-cbc-hmac-sha1-kd encryption type " +
- "are not supported for TLS Kerberos cipher suites");
- }
-
- this.protocolVersion = protocolVersion;
- preMaster = generatePreMaster(generator, protocolVersion);
-
- // Encrypt premaster secret
- try {
- EncryptedData eData = new EncryptedData(sessionKey, preMaster,
- KeyUsage.KU_UNKNOWN);
- encrypted = eData.getBytes(); // not ASN.1 encoded.
-
- } catch (KrbException e) {
- throw (SSLKeyException)new SSLKeyException
- ("Kerberos premaster secret error").initCause(e);
- }
- }
-
- /*
- * Constructor used by server to decrypt encrypted premaster secret.
- * The protocol version in preMaster[0,1] must match either currentVersion
- * or clientVersion, otherwise, the premaster secret is set to
- * a random one to foil possible attack.
- *
- * @param currentVersion version of protocol being used
- * @param clientVersion version requested by client
- * @param generator random number generator used to generate
- * bogus premaster secret if premaster secret verification fails
- * @param input input stream from which to read the encrypted
- * premaster secret
- * @param sessionKey Kerberos session key to be used for decryption
- */
- KerberosPreMasterSecret(ProtocolVersion currentVersion,
- ProtocolVersion clientVersion,
- SecureRandom generator, HandshakeInStream input,
- EncryptionKey sessionKey) throws IOException {
-
- // Extract encrypted premaster secret from message
- encrypted = input.getBytes16();
-
- if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
- if (encrypted != null) {
- Debug.println(System.out,
- "encrypted premaster secret", encrypted);
- }
- }
-
- if (sessionKey.getEType() ==
- EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
- throw new IOException(
- "session keys with des3-cbc-hmac-sha1-kd encryption type " +
- "are not supported for TLS Kerberos cipher suites");
- }
-
- // Decrypt premaster secret
- try {
- EncryptedData data = new EncryptedData(sessionKey.getEType(),
- null /* optional kvno */, encrypted);
-
- byte[] temp = data.decrypt(sessionKey, KeyUsage.KU_UNKNOWN);
- if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
- if (encrypted != null) {
- Debug.println(System.out,
- "decrypted premaster secret", temp);
- }
- }
-
- // Remove padding bytes after decryption. Only DES and DES3 have
- // paddings and we don't support DES3 in TLS (see above)
-
- if (temp.length == 52 &&
- data.getEType() == EncryptedData.ETYPE_DES_CBC_CRC) {
- // For des-cbc-crc, 4 paddings. Value can be 0x04 or 0x00.
- if (paddingByteIs(temp, 52, (byte)4) ||
- paddingByteIs(temp, 52, (byte)0)) {
- temp = Arrays.copyOf(temp, 48);
- }
- } else if (temp.length == 56 &&
- data.getEType() == EncryptedData.ETYPE_DES_CBC_MD5) {
- // For des-cbc-md5, 8 paddings with 0x08, or no padding
- if (paddingByteIs(temp, 56, (byte)8)) {
- temp = Arrays.copyOf(temp, 48);
- }
- }
-
- preMaster = temp;
-
- protocolVersion = ProtocolVersion.valueOf(preMaster[0],
- preMaster[1]);
- if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
- System.out.println("Kerberos PreMasterSecret version: "
- + protocolVersion);
- }
- } catch (Exception e) {
- // catch exception & process below
- preMaster = null;
- protocolVersion = currentVersion;
- }
-
- // check if the premaster secret version is ok
- // the specification says that it must be the maximum version supported
- // by the client from its ClientHello message. However, many
- // old implementations send the negotiated version, so accept both
- // for SSL v3.0 and TLS v1.0.
- // NOTE that we may be comparing two unsupported version numbers in
- // the second case, which is why we cannot use object references
- // equality in this special case
- boolean versionMismatch = (protocolVersion.v != clientVersion.v);
-
- /*
- * we never checked the client_version in server side
- * for TLS v1.0 and SSL v3.0. For compatibility, we
- * maintain this behavior.
- */
- if (versionMismatch && (clientVersion.v <= 0x0301)) {
- versionMismatch = (protocolVersion.v != currentVersion.v);
- }
-
- /*
- * Bogus decrypted ClientKeyExchange? If so, conjure a
- * a random preMaster secret that will fail later during
- * Finished message processing. This is a countermeasure against
- * the "interactive RSA PKCS#1 encryption envelop attack" reported
- * in June 1998. Preserving the executation path will
- * mitigate timing attacks and force consistent error handling
- * that will prevent an attacking client from differentiating
- * different kinds of decrypted ClientKeyExchange bogosities.
- */
- if ((preMaster == null) || (preMaster.length != 48)
- || versionMismatch) {
- if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
- System.out.println("Kerberos PreMasterSecret error, "
- + "generating random secret");
- if (preMaster != null) {
- Debug.println(System.out, "Invalid secret", preMaster);
- }
- }
-
- /*
- * Randomize the preMaster secret with the
- * ClientHello.client_version, as will produce invalid master
- * secret to prevent the attacks.
- */
- preMaster = generatePreMaster(generator, clientVersion);
- protocolVersion = clientVersion;
- }
- }
-
- /**
- * Checks if all paddings of data are b
- * @param data the block with padding
- * @param len length of data, >= 48
- * @param b expected padding byte
- */
- private static boolean paddingByteIs(byte[] data, int len, byte b) {
- for (int i=48; i<len; i++) {
- if (data[i] != b) return false;
- }
- return true;
- }
-
- /*
- * Used by server to generate premaster secret in case of
- * problem decoding ticket.
- *
- * @param protocolVersion used for preMaster[0,1]
- * @param generator random number generator to use for generating secret.
- */
- KerberosPreMasterSecret(ProtocolVersion protocolVersion,
- SecureRandom generator) {
-
- this.protocolVersion = protocolVersion;
- preMaster = generatePreMaster(generator, protocolVersion);
- }
-
- private static byte[] generatePreMaster(SecureRandom rand,
- ProtocolVersion ver) {
-
- byte[] pm = new byte[48];
- rand.nextBytes(pm);
- pm[0] = ver.major;
- pm[1] = ver.minor;
-
- return pm;
- }
-
- // Clone not needed; internal use only
- byte[] getUnencrypted() {
- return preMaster;
- }
-
- // Clone not needed; internal use only
- byte[] getEncrypted() {
- return encrypted;
- }
-}
--- a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java Thu Jun 04 09:31:49 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl.krb5;
-
-import java.security.AccessControlContext;
-import java.security.Permission;
-import java.security.Principal;
-import java.util.Set;
-import javax.crypto.SecretKey;
-import javax.security.auth.Subject;
-import javax.security.auth.kerberos.KerberosKey;
-import javax.security.auth.kerberos.KeyTab;
-import javax.security.auth.kerberos.ServicePermission;
-import javax.security.auth.login.LoginException;
-
-import sun.security.jgss.GSSCaller;
-import sun.security.jgss.krb5.Krb5Util;
-import sun.security.jgss.krb5.ServiceCreds;
-import sun.security.krb5.PrincipalName;
-import sun.security.ssl.Krb5Proxy;
-
-/**
- * An implementation of Krb5Proxy that simply delegates to the appropriate
- * Kerberos APIs.
- */
-public class Krb5ProxyImpl implements Krb5Proxy {
-
- public Krb5ProxyImpl() { }
-
- @Override
- public Subject getClientSubject(AccessControlContext acc)
- throws LoginException {
- return Krb5Util.getSubject(GSSCaller.CALLER_SSL_CLIENT, acc);
- }
-
- @Override
- public Subject getServerSubject(AccessControlContext acc)
- throws LoginException {
- return Krb5Util.getSubject(GSSCaller.CALLER_SSL_SERVER, acc);
- }
-
- @Override
- public Object getServiceCreds(AccessControlContext acc)
- throws LoginException {
- ServiceCreds serviceCreds =
- Krb5Util.getServiceCreds(GSSCaller.CALLER_SSL_SERVER, null, acc);
- return serviceCreds;
- }
-
- @Override
- public String getServerPrincipalName(Object serviceCreds) {
- return ((ServiceCreds)serviceCreds).getName();
- }
-
- @Override
- public String getPrincipalHostName(Principal principal) {
- if (principal == null) {
- return null;
- }
- String hostName = null;
- try {
- PrincipalName princName =
- new PrincipalName(principal.getName(),
- PrincipalName.KRB_NT_SRV_HST);
- String[] nameParts = princName.getNameStrings();
- if (nameParts.length >= 2) {
- hostName = nameParts[1];
- }
- } catch (Exception e) {
- // ignore
- }
- return hostName;
- }
-
-
- @Override
- public Permission getServicePermission(String principalName,
- String action) {
- return new ServicePermission(principalName, action);
- }
-
- @Override
- public boolean isRelated(Subject subject, Principal princ) {
- if (princ == null) return false;
- Set<Principal> principals =
- subject.getPrincipals(Principal.class);
- if (principals.contains(princ)) {
- // bound to this principal
- return true;
- }
- for (KeyTab pc: subject.getPrivateCredentials(KeyTab.class)) {
- if (!pc.isBound()) {
- return true;
- }
- }
- return false;
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/AbstractCharsetProvider.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2000, 2011, 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.nio.cs.ext;
+
+import java.lang.ref.SoftReference;
+import java.nio.charset.Charset;
+import java.nio.charset.spi.CharsetProvider;
+import java.util.ArrayList;
+import java.util.TreeMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import sun.misc.ASCIICaseInsensitiveComparator;
+
+
+/**
+ * Abstract base class for charset providers.
+ *
+ * @author Mark Reinhold
+ */
+
+public class AbstractCharsetProvider
+ extends CharsetProvider
+{
+
+ /* Maps canonical names to class names
+ */
+ private Map<String,String> classMap
+ = new TreeMap<>(ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER);
+
+ /* Maps alias names to canonical names
+ */
+ private Map<String,String> aliasMap
+ = new TreeMap<>(ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER);
+
+ /* Maps canonical names to alias-name arrays
+ */
+ private Map<String,String[]> aliasNameMap
+ = new TreeMap<>(ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER);
+
+ /* Maps canonical names to soft references that hold cached instances
+ */
+ private Map<String,SoftReference<Charset>> cache
+ = new TreeMap<>(ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER);
+
+ private String packagePrefix;
+
+ protected AbstractCharsetProvider() {
+ packagePrefix = "sun.nio.cs";
+ }
+
+ protected AbstractCharsetProvider(String pkgPrefixName) {
+ packagePrefix = pkgPrefixName;
+ }
+
+ /* Add an entry to the given map, but only if no mapping yet exists
+ * for the given name.
+ */
+ private static <K,V> void put(Map<K,V> m, K name, V value) {
+ if (!m.containsKey(name))
+ m.put(name, value);
+ }
+
+ private static <K,V> void remove(Map<K,V> m, K name) {
+ V x = m.remove(name);
+ assert (x != null);
+ }
+
+ /* Declare support for the given charset
+ */
+ protected void charset(String name, String className, String[] aliases) {
+ synchronized (this) {
+ put(classMap, name, className);
+ for (int i = 0; i < aliases.length; i++)
+ put(aliasMap, aliases[i], name);
+ put(aliasNameMap, name, aliases);
+ cache.clear();
+ }
+ }
+
+ protected void deleteCharset(String name, String[] aliases) {
+ synchronized (this) {
+ remove(classMap, name);
+ for (int i = 0; i < aliases.length; i++)
+ remove(aliasMap, aliases[i]);
+ remove(aliasNameMap, name);
+ cache.clear();
+ }
+ }
+
+ protected boolean hasCharset(String name) {
+ synchronized (this) {
+ return classMap.containsKey(name);
+ }
+ }
+
+ /* Late initialization hook, needed by some providers
+ */
+ protected void init() { }
+
+ private String canonicalize(String charsetName) {
+ String acn = aliasMap.get(charsetName);
+ return (acn != null) ? acn : charsetName;
+ }
+
+ private Charset lookup(String csn) {
+
+ // Check cache first
+ SoftReference<Charset> sr = cache.get(csn);
+ if (sr != null) {
+ Charset cs = sr.get();
+ if (cs != null)
+ return cs;
+ }
+
+ // Do we even support this charset?
+ String cln = classMap.get(csn);
+
+ if (cln == null)
+ return null;
+
+ // Instantiate the charset and cache it
+ try {
+
+ Class<?> c = Class.forName(packagePrefix + "." + cln,
+ true,
+ this.getClass().getClassLoader());
+
+ Charset cs = (Charset)c.newInstance();
+ cache.put(csn, new SoftReference<Charset>(cs));
+ return cs;
+ } catch (ClassNotFoundException x) {
+ return null;
+ } catch (IllegalAccessException x) {
+ return null;
+ } catch (InstantiationException x) {
+ return null;
+ }
+ }
+
+ public final Charset charsetForName(String charsetName) {
+ synchronized (this) {
+ init();
+ return lookup(canonicalize(charsetName));
+ }
+ }
+
+ public final Iterator<Charset> charsets() {
+
+ final ArrayList<String> ks;
+ synchronized (this) {
+ init();
+ ks = new ArrayList<>(classMap.keySet());
+ }
+
+ return new Iterator<Charset>() {
+ Iterator<String> i = ks.iterator();
+
+ public boolean hasNext() {
+ return i.hasNext();
+ }
+
+ public Charset next() {
+ String csn = i.next();
+ synchronized (AbstractCharsetProvider.this) {
+ return lookup(csn);
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ public final String[] aliases(String charsetName) {
+ synchronized (this) {
+ init();
+ return aliasNameMap.get(charsetName);
+ }
+ }
+
+}
--- a/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/ExtendedCharsets.java.template Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/ExtendedCharsets.java.template Thu Jun 04 18:49:37 2015 -0700
@@ -32,10 +32,8 @@
import java.lang.ref.SoftReference;
import java.nio.charset.Charset;
import java.nio.charset.spi.CharsetProvider;
-import sun.nio.cs.AbstractCharsetProvider;
import java.security.AccessController;
import java.security.PrivilegedAction;
-import sun.nio.cs.AbstractCharsetProvider;
/**
* Provider for extended charsets.
--- a/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/NativeGCMCipher.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/NativeGCMCipher.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -125,9 +125,7 @@
if (ibuffer != null) {
result += ibuffer.size();
}
- if (isDoFinal) {
- result -= tagLen/8;
- }
+ result -= tagLen/8;
}
if (result < 0) {
result = 0;
--- a/jdk/test/ProblemList.txt Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/ProblemList.txt Thu Jun 04 18:49:37 2015 -0700
@@ -222,8 +222,7 @@
sun/security/pkcs11/ec/TestKeyFactory.java generic-all
# 7164518: no PortUnreachableException on Mac
-# 8051952: Unreachable.java test failing on Windows
-sun/security/krb5/auto/Unreachable.java windows-all,macosx-all
+sun/security/krb5/auto/Unreachable.java macosx-all
# 8058849
sun/security/krb5/config/dns.sh generic-all
--- a/jdk/test/TEST.ROOT Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/TEST.ROOT Thu Jun 04 18:49:37 2015 -0700
@@ -12,7 +12,7 @@
keys=2d dnd i18n intermittent randomness
# Tests that must run in othervm mode
-othervm.dirs=java/awt java/beans javax/accessibility javax/imageio javax/sound javax/print javax/management com/sun/awt sun/awt sun/java2d sun/pisces javax/xml/jaxp/testng/validation
+othervm.dirs=java/awt java/beans javax/accessibility javax/imageio javax/sound javax/print javax/management com/sun/awt sun/awt sun/java2d sun/pisces javax/xml/jaxp/testng/validation java/lang/ProcessHandle
# Tests that cannot run concurrently
exclusiveAccess.dirs=java/rmi/Naming java/util/prefs sun/management/jmxremote sun/tools/jstatd sun/security/mscapi java/util/stream javax/rmi
--- a/jdk/test/com/sun/jdi/AllLineLocations.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/com/sun/jdi/AllLineLocations.java Thu Jun 04 18:49:37 2015 -0700
@@ -27,7 +27,6 @@
* @summary Test ReferenceType.allLineLocations
* @author Gordon Hirsch
*
- * @library scaffold
* @modules jdk.jdi
* @run build JDIScaffold VMConnection
* @run compile -g RefTypes.java
--- a/jdk/test/com/sun/jdi/ClassesByName.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/com/sun/jdi/ClassesByName.java Thu Jun 04 18:49:37 2015 -0700
@@ -26,7 +26,6 @@
* @bug 4287992
* @author Robert Field
*
- * @library scaffold
* @modules jdk.jdi
* @run build JDIScaffold VMConnection
* @run compile -g HelloWorld.java
--- a/jdk/test/com/sun/jdi/ExceptionEvents.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/com/sun/jdi/ExceptionEvents.java Thu Jun 04 18:49:37 2015 -0700
@@ -28,7 +28,6 @@
*
* @author Robert Field
*
- * @library scaffold
* @modules jdk.jdi
* @run build TestScaffold VMConnection
* @run compile -g ExceptionEvents.java
--- a/jdk/test/com/sun/jdi/FilterMatch.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/com/sun/jdi/FilterMatch.java Thu Jun 04 18:49:37 2015 -0700
@@ -28,7 +28,6 @@
*
* @author Robert Field/Jim Holmlund
*
- * @library scaffold
* @modules jdk.jdi
* @run build JDIScaffold VMConnection
* @run compile -g HelloWorld.java
--- a/jdk/test/com/sun/jdi/FilterNoMatch.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/com/sun/jdi/FilterNoMatch.java Thu Jun 04 18:49:37 2015 -0700
@@ -28,7 +28,6 @@
*
* @author Robert Field/Jim Holmlund
*
- * @library scaffold
* @modules jdk.jdi
* @run build JDIScaffold VMConnection
* @run compile -g HelloWorld.java
--- a/jdk/test/com/sun/jdi/LaunchCommandLine.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/com/sun/jdi/LaunchCommandLine.java Thu Jun 04 18:49:37 2015 -0700
@@ -27,7 +27,6 @@
* @summary Test launcher command line construction
* @author Gordon Hirsch
*
- * @library scaffold
* @modules jdk.jdi
* @run build JDIScaffold VMConnection
* @run compile -g HelloWorld.java
--- a/jdk/test/com/sun/jdi/ModificationWatchpoints.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/com/sun/jdi/ModificationWatchpoints.java Thu Jun 04 18:49:37 2015 -0700
@@ -29,7 +29,6 @@
* @author Daniel Prusa (or someone in the FFJ group)
* @author Robert Field (modified to JDIScaffold)
*
- * @library scaffold
* @modules jdk.jdi
* @run build JDIScaffold VMConnection
* @run compile -g ModificationWatchpoints.java
--- a/jdk/test/com/sun/jdi/NativeInstanceFilter.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/com/sun/jdi/NativeInstanceFilter.java Thu Jun 04 18:49:37 2015 -0700
@@ -28,7 +28,6 @@
*
* @author Keith McGuigan
*
- * @library scaffold
* @modules jdk.jdi
* @run build JDIScaffold VMConnection
* @compile -XDignore.symbol.file NativeInstanceFilterTarg.java
--- a/jdk/test/com/sun/jdi/UnpreparedByName.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/com/sun/jdi/UnpreparedByName.java Thu Jun 04 18:49:37 2015 -0700
@@ -28,7 +28,6 @@
* won't be returned by classesByName.
* @author Robert Field
*
- * @library scaffold
* @modules jdk.jdi
* @run build JDIScaffold VMConnection
* @run compile -g InnerTarg.java
--- a/jdk/test/com/sun/jdi/UnpreparedClasses.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/com/sun/jdi/UnpreparedClasses.java Thu Jun 04 18:49:37 2015 -0700
@@ -28,7 +28,6 @@
* loaded class list are prepared classes.
* @author Robert Field
*
- * @library scaffold
* @modules jdk.jdi
* @run build JDIScaffold VMConnection
* @run compile -g InnerTarg.java
--- a/jdk/test/com/sun/jdi/Vars.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/com/sun/jdi/Vars.java Thu Jun 04 18:49:37 2015 -0700
@@ -27,7 +27,6 @@
*
* @author Robert Field
*
- * @library scaffold
* @modules jdk.jdi
* @run build JDIScaffold VMConnection
* @run compile -g Vars.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Component/SetEnabledPerformance/SetEnabledPerformance.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.Robot;
+
+import javax.swing.JButton;
+import javax.swing.SwingUtilities;
+
+/**
+ * @test
+ * @bug 8071306
+ * @author Sergey Bylokhov
+ */
+public final class SetEnabledPerformance {
+
+ private static Frame frame;
+
+ private static void createAndShowGUI() {
+ frame = new Frame();
+ frame.setLayout(new FlowLayout(FlowLayout.CENTER, 25, 0));
+ frame.setSize(600, 600);
+ frame.setLocationRelativeTo(null);
+ for (int i = 1; i < 10001; ++i) {
+ frame.add(new JButton("Button " + i));
+ }
+ frame.setVisible(true);
+ }
+
+ public static void main(final String[] args) throws Exception {
+ SwingUtilities.invokeAndWait(() -> createAndShowGUI());
+ final Robot robot = new Robot();
+ robot.waitForIdle();
+ robot.mouseMove(frame.getX() + 15, frame.getY() + 300);
+ robot.waitForIdle();
+ SwingUtilities.invokeAndWait(() -> {
+ long m = System.currentTimeMillis();
+ for (final Component comp : frame.getComponents()) {
+ comp.setEnabled(false);
+ }
+ m = System.currentTimeMillis() - m;
+ System.err.println("Disabled in " + m + " ms");
+ frame.dispose();
+ // we should be much faster, but leaves 1000 for the slow systems
+ if (m > 1000) {
+ throw new RuntimeException("Too slow");
+ }
+ });
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/EventQueue/6980209/bug6980209.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ @bug 6980209
+ @summary Make tracking SecondaryLoop.enter/exit methods easier
+ @author Semyon Sadetsky
+ */
+
+import sun.util.logging.PlatformLogger;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+
+public class bug6980209 implements ActionListener {
+ private final static PlatformLogger log =
+ PlatformLogger.getLogger("java.awt.event.WaitDispatchSupport");
+ public static final int ATTEMPTS = 100;
+ public static final int EVENTS = 5;
+
+ private static boolean runInEDT;
+ private static JFrame frame;
+ private static int disorderCounter = 0;
+ private static Boolean enterReturn;
+ private static Boolean exitReturn;
+ private static int dispatchedEvents;
+
+ public static void main(String[] args) throws Exception {
+ System.out.println(
+ "PLEASE DO NOT TOUCH KEYBOARD AND MOUSE DURING THE TEST RUN!");
+ // log.setLevel(PlatformLogger.Level.FINE);
+ // log.setLevel(PlatformLogger.Level.FINEST);
+ try {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ frame = new JFrame();
+ frame.setUndecorated(true);
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setup(frame);
+ }
+ });
+ testExitBeforeEnter();
+ System.out.println("Run random test in EDT");
+ runInEDT = true;
+ testRandomly();
+ System.out.println("Run random test in another thread");
+ runInEDT = false;
+ testRandomly();
+ System.out.println("ok");
+
+ } finally {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ frame.dispose();
+ }
+ });
+ }
+ }
+
+ private static void testExitBeforeEnter() throws Exception {
+ final SecondaryLoop loop =
+ Toolkit.getDefaultToolkit().getSystemEventQueue()
+ .createSecondaryLoop();
+ loop.exit();
+ Robot robot = new Robot();
+ robot.mouseWheel(1);
+ robot.waitForIdle();
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ if(loop.enter()) {
+ throw new RuntimeException("Wrong enter() return value");
+ }
+ }
+ });
+ }
+
+ private static void testRandomly() throws AWTException {
+ disorderCounter = 0;
+ final Robot robot = new Robot();
+ for (int i = 0; i < ATTEMPTS; i++) {
+ enterReturn = null;
+ exitReturn = null;
+ dispatchedEvents = 0;
+ synchronized (bug6980209.class) {
+ try {
+ for (int j = 0; j < EVENTS; j++) {
+ robot.keyPress(KeyEvent.VK_1);
+ robot.keyRelease(KeyEvent.VK_1);
+ }
+
+ // trigger the button action that starts secondary loop
+ robot.keyPress(KeyEvent.VK_SPACE);
+ robot.keyRelease(KeyEvent.VK_SPACE);
+
+ for (int j = 0; j < EVENTS; j++) {
+ robot.keyPress(KeyEvent.VK_1);
+ robot.keyRelease(KeyEvent.VK_1);
+ }
+ long time = System.nanoTime();
+ // wait for enter() returns
+ bug6980209.class.wait(1000);
+ if (enterReturn == null) {
+ System.out.println("wait time=" +
+ ((System.nanoTime() - time) / 1E9) +
+ " seconds");
+ throw new RuntimeException(
+ "It seems the secondary loop will never end");
+ }
+ if (!enterReturn) disorderCounter++;
+
+ robot.waitForIdle();
+ if (dispatchedEvents <
+ 2 * EVENTS) { //check that all events are dispatched
+ throw new RuntimeException(
+ "KeyEvent.VK_1 has been lost!");
+ }
+
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted!");
+ }
+ }
+ }
+ if (disorderCounter == 0) {
+ System.out.println(
+ "Zero disordered enter/exit caught. It is recommended to run scenario again");
+ } else {
+ System.out.println(
+ "Disordered calls is " + disorderCounter + " from " +
+ ATTEMPTS);
+ }
+ }
+
+ private static void setup(final JFrame frame) {
+ JButton jButton = new JButton("Button");
+ frame.getContentPane().add(jButton);
+ jButton.addActionListener(new bug6980209());
+ frame.pack();
+ frame.setVisible(true);
+ jButton.setFocusable(true);
+ jButton.requestFocus();
+ jButton.addKeyListener(new KeyListener() {
+ @Override
+ public void keyTyped(KeyEvent e) {
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyChar() == '1') dispatchedEvents++;
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ if (e.getKeyChar() == '1') dispatchedEvents++;
+ }
+ });
+ }
+
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (runInEDT) {
+ runSecondaryLoop();
+ return;
+ }
+ new Thread("Secondary loop run thread") {
+ @Override
+ public void run() {
+ runSecondaryLoop();
+ }
+ }.start();
+ }
+
+ private static void runSecondaryLoop() {
+ log.fine("\n---TEST START---");
+
+ final SecondaryLoop loop =
+ Toolkit.getDefaultToolkit().getSystemEventQueue()
+ .createSecondaryLoop();
+
+ final Object LOCK = new Object(); //lock to start simultaneously
+ Thread exitThread = new Thread("Exit thread") {
+ @Override
+ public void run() {
+ synchronized (LOCK) {
+ LOCK.notify();
+ }
+ Thread.yield();
+ exitReturn = loop.exit();
+ log.fine("exit() returns " + exitReturn);
+ }
+ };
+
+ synchronized (LOCK) {
+ try {
+ exitThread.start();
+ LOCK.wait();
+ } catch (InterruptedException e1) {
+ throw new RuntimeException("What?");
+ }
+ }
+
+ enterReturn = loop.enter();
+ log.fine("enter() returns " + enterReturn);
+
+ try {
+ exitThread.join();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("What?");
+ }
+ synchronized (bug6980209.class) {
+ bug6980209.class.notifyAll();
+ }
+ log.fine("\n---TEST END---");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/FileDialog/8003399/bug8003399.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ @bug 8003399
+ @summary JFileChooser gives wrong path to selected file when saving to Libraries folder on Windows 7
+ @author Semyon Sadetsky
+ @library /lib/testlibrary
+ @build jdk.testlibrary.OSInfo
+ @run main bug8003399
+ */
+
+import jdk.testlibrary.OSInfo;
+
+import javax.swing.filechooser.FileSystemView;
+import java.io.File;
+
+public class bug8003399 {
+
+ public static void main(String[] args) throws Exception {
+ if (OSInfo.getOSType() == OSInfo.OSType.WINDOWS &&
+ OSInfo.getWindowsVersion().compareTo(OSInfo.WINDOWS_VISTA) > 0 ) {
+ FileSystemView fsv = FileSystemView.getFileSystemView();
+ for (File file : fsv.getFiles(fsv.getHomeDirectory(), false)) {
+ if(file.isDirectory()) {
+ for (File file1 : fsv.getFiles(file, false)) {
+ if(file1.isDirectory())
+ {
+ String path = file1.getPath();
+ if(path.startsWith("::{") &&
+ path.toLowerCase().endsWith(".library-ms")) {
+ throw new RuntimeException("Unconverted library link found");
+ }
+ }
+ }
+ }
+ }
+ }
+ System.out.println("ok");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Frame/MaximizedToUnmaximized/MaximizedToUnmaximized.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.awt.Frame;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.Toolkit;
+
+/**
+ * @test
+ * @bug 8065739
+ * @summary [macosx] Frame warps to lower left of screen when displayed
+ * @author Alexandr Scherbatiy
+ */
+public class MaximizedToUnmaximized {
+
+ public static void main(String[] args) throws Exception {
+ testFrame(false);
+ testFrame(true);
+ }
+
+ static void testFrame(boolean isUndecorated) throws Exception {
+ Frame frame = new Frame();
+ try {
+ Robot robot = new Robot();
+ robot.setAutoDelay(100);
+
+ frame.setUndecorated(isUndecorated);
+ GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
+ .getDefaultScreenDevice().getDefaultConfiguration();
+ Rectangle bounds = gc.getBounds();
+ Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
+ int x = bounds.x + insets.left;
+ int y = bounds.y + insets.top;
+ int width = bounds.width - insets.left - insets.right;
+ int height = bounds.height - insets.top - insets.bottom;
+ Rectangle rect = new Rectangle(x, y, width, height);
+ frame.pack();
+ frame.setBounds(rect);
+ frame.setVisible(true);
+ robot.waitForIdle();
+ robot.delay(500);
+
+ if (frame.getWidth() <= width / 2
+ || frame.getHeight() <= height / 2) {
+ throw new RuntimeException("Frame size is small!");
+ }
+
+ if (!isUndecorated && frame.getExtendedState() != Frame.MAXIMIZED_BOTH) {
+ throw new RuntimeException("Frame state does not equal"
+ + " MAXIMIZED_BOTH!");
+ }
+ } finally {
+ frame.dispose();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Frame/SetMaximizedBounds/MaximizedMovedWindow.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.awt.*;
+
+/*
+ * @test
+ * @bug 8065739
+ * @summary Moved window is maximazed to new screen
+ * @author Alexandr Scherbatiy
+ *
+ * @run main MaximizedMovedWindow
+ */
+public class MaximizedMovedWindow {
+
+ public static void main(String[] args) throws Exception {
+
+ //Supported platforms are Windows and OS X.
+ String os = System.getProperty("os.name").toLowerCase();
+ if (!os.contains("os x")) {
+ return;
+ }
+
+ if (!Toolkit.getDefaultToolkit().
+ isFrameStateSupported(Frame.MAXIMIZED_BOTH)) {
+ return;
+ }
+
+ GraphicsEnvironment ge = GraphicsEnvironment.
+ getLocalGraphicsEnvironment();
+
+ if (ge.isHeadlessInstance()) {
+ return;
+ }
+
+ GraphicsDevice[] devices = ge.getScreenDevices();
+
+ if (devices.length < 2) {
+ return;
+ }
+
+ Frame frame = null;
+ try {
+
+ GraphicsConfiguration gc1 = devices[0].getDefaultConfiguration();
+ GraphicsConfiguration gc2 = devices[1].getDefaultConfiguration();
+
+ Robot robot = new Robot();
+ robot.setAutoDelay(50);
+
+ frame = new Frame();
+ Rectangle maxArea1 = getMaximizedScreenArea(gc1);
+ frame.setBounds(getSmallerRectangle(maxArea1));
+ frame.setVisible(true);
+ robot.waitForIdle();
+
+ frame.setExtendedState(Frame.MAXIMIZED_BOTH);
+ robot.waitForIdle();
+ robot.delay(1000);
+
+ Rectangle bounds = frame.getBounds();
+ if (!bounds.equals(maxArea1)) {
+ throw new RuntimeException("The bounds of the Frame do not equal"
+ + " to screen 1 size");
+ }
+
+ frame.setExtendedState(Frame.NORMAL);
+ robot.waitForIdle();
+ robot.delay(1000);
+
+ Rectangle maxArea2 = getMaximizedScreenArea(gc2);
+ frame.setBounds(getSmallerRectangle(maxArea2));
+ robot.waitForIdle();
+ robot.delay(1000);
+
+ frame.setExtendedState(Frame.MAXIMIZED_BOTH);
+ robot.waitForIdle();
+ robot.delay(1000);
+
+ bounds = frame.getBounds();
+ if (!bounds.equals(maxArea2)) {
+ throw new RuntimeException("The bounds of the Frame do not equal"
+ + " to screen 2 size");
+ }
+ } finally {
+ if (frame != null) {
+ frame.dispose();
+ }
+ }
+ }
+
+ static Rectangle getSmallerRectangle(Rectangle rect) {
+ return new Rectangle(
+ rect.x + rect.width / 6,
+ rect.y + rect.height / 6,
+ rect.width / 3,
+ rect.height / 3);
+ }
+ static Rectangle getMaximizedScreenArea(GraphicsConfiguration gc) {
+ Rectangle bounds = gc.getBounds();
+ Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
+ return new Rectangle(
+ bounds.x + insets.left,
+ bounds.y + insets.top,
+ bounds.width - insets.left - insets.right,
+ bounds.height - insets.top - insets.bottom);
+ }
+}
--- a/jdk/test/java/awt/Frame/SetMaximizedBounds/SetMaximizedBounds.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/java/awt/Frame/SetMaximizedBounds/SetMaximizedBounds.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,67 +22,108 @@
*/
import java.awt.*;
-
/*
* @test
* @summary When Frame.setExtendedState(Frame.MAXIMIZED_BOTH)
* is called for a Frame after been called setMaximizedBounds() with
* certain value, Frame bounds must equal to this value.
*
- * @library ../../../../lib/testlibrary
- * @build ExtendedRobot
* @run main SetMaximizedBounds
*/
public class SetMaximizedBounds {
- Frame frame;
- Rectangle bound;
- boolean supported;
- ExtendedRobot robot;
- static Rectangle max = new Rectangle(100,100,400,400);
+ public static void main(String[] args) throws Exception {
+
+ //Supported platforms are Windows and OS X.
+ String os = System.getProperty("os.name").toLowerCase();
+ if (!os.contains("windows") && !os.contains("os x")) {
+ return;
+ }
+
+ if (!Toolkit.getDefaultToolkit().
+ isFrameStateSupported(Frame.MAXIMIZED_BOTH)) {
+ return;
+ }
- public void doTest() throws Exception {
- robot = new ExtendedRobot();
+ GraphicsEnvironment ge = GraphicsEnvironment.
+ getLocalGraphicsEnvironment();
+
+ if (ge.isHeadlessInstance()) {
+ return;
+ }
+
+ for (GraphicsDevice gd : ge.getScreenDevices()) {
+ for (GraphicsConfiguration gc : gd.getConfigurations()) {
+ testMaximizedBounds(gc);
+ }
+ }
+ }
- EventQueue.invokeAndWait( () -> {
- frame = new Frame( "TestFrame ");
- frame.setLayout(new FlowLayout());
+ static void testMaximizedBounds(GraphicsConfiguration gc) throws Exception {
+
+ Frame frame = null;
+ try {
+
+ Rectangle maxArea = getMaximizedScreenArea(gc);
+
+ Robot robot = new Robot();
+ robot.setAutoDelay(50);
- if (Toolkit.getDefaultToolkit().isFrameStateSupported(Frame.MAXIMIZED_BOTH)) {
- supported = true;
- frame.setMaximizedBounds(max);
- } else {
- supported = false;
+ frame = new Frame();
+ Rectangle maximizedBounds = new Rectangle(
+ maxArea.x + maxArea.width / 6,
+ maxArea.y + maxArea.height / 6,
+ maxArea.width / 3,
+ maxArea.height / 3);
+ frame.setMaximizedBounds(maximizedBounds);
+ frame.setSize(maxArea.width / 8, maxArea.height / 8);
+ frame.setVisible(true);
+ robot.waitForIdle();
+
+ frame.setExtendedState(Frame.MAXIMIZED_BOTH);
+ robot.waitForIdle();
+ robot.delay(1000);
+
+ Rectangle bounds = frame.getBounds();
+ if (!bounds.equals(maximizedBounds)) {
+ throw new RuntimeException("The bounds of the Frame do not equal to what"
+ + " is specified when the frame is in Frame.MAXIMIZED_BOTH state");
}
- frame.setSize(200, 200);
- frame.setVisible(true);
- });
+ frame.setExtendedState(Frame.NORMAL);
+ robot.waitForIdle();
+ robot.delay(1000);
- robot.waitForIdle(2000);
- if (supported) {
- EventQueue.invokeAndWait( () -> {
- frame.setExtendedState(Frame.MAXIMIZED_BOTH);
- });
- robot.waitForIdle(2000);
- bound = frame.getBounds();
- if(!bound.equals(max))
+ maximizedBounds = new Rectangle(
+ maxArea.x + maxArea.width / 10,
+ maxArea.y + maxArea.height / 10,
+ maxArea.width / 5,
+ maxArea.height / 5);
+ frame.setMaximizedBounds(maximizedBounds);
+ frame.setExtendedState(Frame.MAXIMIZED_BOTH);
+ robot.waitForIdle();
+ robot.delay(1000);
+
+ bounds = frame.getBounds();
+ if (!bounds.equals(maximizedBounds)) {
throw new RuntimeException("The bounds of the Frame do not equal to what"
- + " is specified when the frame is in Frame.MAXIMIZED_BOTH state");
- } else {
- System.out.println("Frame.MAXIMIZED_BOTH not supported");
+ + " is specified when the frame is in Frame.MAXIMIZED_BOTH state");
+ }
+ } finally {
+ if (frame != null) {
+ frame.dispose();
+ }
}
-
- frame.dispose();
}
- public static void main(String[] args) throws Exception {
- String os = System.getProperty("os.name").toLowerCase();
- System.out.println(os);
- if (os.contains("windows") || os.contains("os x"))
- new SetMaximizedBounds().doTest();
- else
- System.out.println("Platform "+os+" is not supported. Supported platforms are Windows and OS X.");
+ static Rectangle getMaximizedScreenArea(GraphicsConfiguration gc) {
+ Rectangle bounds = gc.getBounds();
+ Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
+ return new Rectangle(
+ bounds.x + insets.left,
+ bounds.y + insets.top,
+ bounds.width - insets.left - insets.right,
+ bounds.height - insets.top - insets.bottom);
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Mouse/MouseDragEvent/MouseDraggedTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+/*
+ * @test
+ * @bug 8080137
+ * @summary Dragged events for extra mouse buttons (4,5,6) are not generated
+ * on JSplitPane
+ * @author alexandr.scherbatiy area=awt.event
+ * @run main MouseDraggedTest
+ */
+public class MouseDraggedTest {
+
+ private static JFrame frame;
+ private static Rectangle frameBounds;
+ private static volatile boolean mouseDragged;
+
+ public static void main(String[] args) throws Exception {
+ try {
+ Robot robot = new Robot();
+ robot.setAutoDelay(50);
+
+ SwingUtilities.invokeAndWait(MouseDraggedTest::createAndShowGUI);
+ robot.waitForIdle();
+
+ SwingUtilities.invokeAndWait(() -> frameBounds = frame.getBounds());
+ robot.waitForIdle();
+
+ for (int i = 1; i <= MouseInfo.getNumberOfButtons(); i++) {
+ testMouseDrag(i, robot);
+ }
+ } finally {
+ SwingUtilities.invokeLater(() -> {
+ if (frame != null) {
+ frame.dispose();
+ }
+ });
+ }
+ }
+
+ private static void testMouseDrag(int button, Robot robot) {
+
+ mouseDragged = false;
+ int x1 = frameBounds.x + frameBounds.width / 4;
+ int y1 = frameBounds.y + frameBounds.height / 4;
+ int x2 = frameBounds.x + frameBounds.width / 2;
+ int y2 = frameBounds.y + frameBounds.height / 2;
+
+ robot.mouseMove(x1, y1);
+ robot.waitForIdle();
+
+ int buttonMask = InputEvent.getMaskForButton(button);
+ robot.mousePress(buttonMask);
+ robot.mouseMove(x2, y2);
+ robot.mouseRelease(buttonMask);
+ robot.waitForIdle();
+
+ if (!mouseDragged) {
+ throw new RuntimeException("Mouse button " + button
+ + " is not dragged");
+ }
+ }
+
+ static void createAndShowGUI() {
+
+ frame = new JFrame();
+ frame.setSize(400, 400);
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.addMouseMotionListener(new MouseAdapter() {
+
+ @Override
+ public void mouseDragged(MouseEvent e) {
+ mouseDragged = true;
+ }
+ });
+ frame.add(panel, BorderLayout.CENTER);
+ frame.setVisible(true);
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/MouseInfo/GetPointerInfoTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ @test
+ @summary unit test for getPointerInfo() from MouseInfo class
+ @author dav@sparc.spb.su: area=
+ @bug 4009555
+ @run main GetPointerInfoTest
+*/
+
+import java.awt.*;
+
+/**
+ * Simply check the result on non-null and results are correct.
+ */
+public class GetPointerInfoTest {
+ private static final String successStage = "Test stage completed.Passed.";
+
+ public static void main(String[] args) throws Exception {
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ GraphicsDevice[] gds = ge.getScreenDevices();
+ int gdslen = gds.length;
+ System.out.println("There are " + gdslen + " Graphics Devices");
+ if (gdslen == 0) {
+ System.out.println("Nothing to be done.");
+ return;
+ }
+ Robot robot = new Robot(gds[0]);
+ robot.setAutoDelay(0);
+ robot.setAutoWaitForIdle(true);
+ robot.delay(10);
+ robot.waitForIdle();
+ Point p = new Point(101, 99);
+ robot.mouseMove(p.x, p.y);
+
+ PointerInfo pi = MouseInfo.getPointerInfo();
+ if (pi == null) {
+ throw new RuntimeException("Test failed. getPointerInfo() returned null value.");
+ } else {
+ System.out.println(successStage);
+ }
+ Point piLocation = pi.getLocation();
+
+ if (piLocation.x != p.x || piLocation.y != p.y) {
+ throw new RuntimeException("Test failed.getPointerInfo() returned incorrect result.");
+ } else {
+ System.out.println(successStage);
+ }
+
+ System.out.println("Test PASSED.");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/MouseInfo/MultiscreenPointerInfo.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ @test
+ @summary unit test for getPointerInfo() from MouseInfo class
+ @author prs@sparc.spb.su: area=
+ @bug 4009555
+ @run main MultiscreenPointerInfo
+*/
+
+import java.awt.*;
+
+/**
+ * Simply check the result on non-null and results are correct.
+ */
+public class MultiscreenPointerInfo
+{
+ private static final String successStage = "Test stage completed.Passed.";
+
+ public static void main(String[] args) throws Exception {
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ GraphicsDevice[] gds = ge.getScreenDevices();
+ int gdslen = gds.length;
+ System.out.println("There are " + gdslen + " Graphics Devices");
+ if (gdslen < 2) {
+ System.out.println("Nothing to be done. PASSED automatically.");
+ return;
+ }
+ Rectangle rx = gds[1].getDefaultConfiguration().getBounds();
+ Robot robot;
+
+ if (rx.x == 0 && rx.y == 0) {
+ // Assuming independent graphics devices
+ robot = new Robot(gds[1]);
+ } else {
+ // Means we have a virtual device
+ robot = new Robot(gds[0]);
+ }
+ robot.setAutoDelay(0);
+ robot.setAutoWaitForIdle(true);
+ robot.delay(10);
+ robot.waitForIdle();
+ Point p = new Point(rx.x + 101, rx.y + 99);
+ robot.mouseMove(p.x, p.y);
+ PointerInfo pi = MouseInfo.getPointerInfo();
+ if (pi == null) {
+ throw new RuntimeException("Test failed. getPointerInfo() returned null value.");
+ } else {
+ System.out.println(successStage);
+ }
+
+ Point piLocation = pi.getLocation();
+
+ if (piLocation.x != p.x || piLocation.y != p.y) {
+ throw new RuntimeException("Test failed.getPointerInfo() returned incorrect location.");
+ } else {
+ System.out.println(successStage);
+ }
+
+ GraphicsDevice dev = pi.getDevice();
+
+ if (dev != gds[1]) {
+ throw new RuntimeException("Test failed.getPointerInfo() returned incorrect device.");
+ } else {
+ System.out.println(successStage);
+ }
+ System.out.println("Test PASSED.");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Robot/RobotWheelTest/RobotWheelTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.awt.Button;
+import java.awt.Frame;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import jdk.testlibrary.OSInfo;
+
+/*
+ * @test 1.2 98/08/05
+ * @bug 4373478 8079255
+ * @summary Test mouse wheel functionality of Robot
+ * @author bchristi: area=Robot
+ * @library ../../../../lib/testlibrary
+ * @build jdk.testlibrary.OSInfo
+ * @run main RobotWheelTest
+ */
+public class RobotWheelTest {
+
+ private static final int NUMTESTS = 20;
+ private static volatile int wheelRotation;
+
+ public static void main(String[] args) throws Exception {
+
+ Frame frame = null;
+ try {
+ int wheelSign = OSInfo.getOSType().equals(OSInfo.OSType.MACOSX) ? -1 : 1;
+
+ frame = new Frame();
+ frame.setSize(200, 200);
+ Button button = new Button("WheelButton");
+ button.addMouseWheelListener(e -> wheelRotation = e.getWheelRotation());
+ frame.add(button);
+ frame.setVisible(true);
+
+ Robot robot = new Robot();
+ robot.setAutoDelay(100);
+ robot.waitForIdle();
+
+ Rectangle bounds = frame.getBounds();
+ int centerX = bounds.x + bounds.width / 2;
+ int centerY = bounds.y + bounds.height / 2;
+ robot.mouseMove(centerX, centerY);
+ robot.waitForIdle();
+
+ for (int i = -NUMTESTS; i <= NUMTESTS; i++) {
+ if (i == 0) {
+ continue;
+ }
+ robot.mouseWheel(i);
+ robot.waitForIdle();
+ if (i != wheelSign * wheelRotation) {
+ throw new RuntimeException("wheelRotation = " + wheelRotation
+ + ", expected value = " + i);
+ }
+ }
+ } finally {
+ if (frame != null) {
+ frame.dispose();
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Scrollbar/ScrollbarMouseWheelTest/ScrollbarMouseWheelTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.*;
+import java.awt.event.*;
+
+/**
+ * @test
+ * @bug 4449139
+ * @summary test MouseWheelEvent generation by Scrollbar component
+ */
+public final class ScrollbarMouseWheelTest
+ implements MouseWheelListener, WindowListener {
+
+ final static String tk = Toolkit.getDefaultToolkit().getClass().getName();
+ final static int REPS = 5;
+ // There is a bug on Windows: 4616935.
+ // Wheel events comes to every component in the hierarchy so we should
+ // check a platform.
+ // There are two scrollbars within one Panel and both accept 5 clicks, so
+ // Panel would accept 5*2 clicks on Windows.
+ final static int PANEL_REPS = tk.equals("sun.awt.windows.WToolkit")? REPS * 2: REPS;
+
+ Scrollbar sb1;
+ Scrollbar sb2;
+ Panel pnl;
+ class Sema {
+ boolean flag;
+ boolean getVal() { return flag;}
+ void setVal(boolean b) { flag = b;}
+ }
+ Sema sema = new Sema();
+
+ Robot robot;
+
+ int sb1upevents, sb2upevents, pnlupevents;
+ int sb1downevents, sb2downevents, pnldownevents;
+
+ public static void main(final String[] args) {
+ new ScrollbarMouseWheelTest().test();
+ }
+
+ public void test() {
+ // Move mouse to upper-right area
+ try {
+ robot = new Robot();
+ } catch (AWTException e) {
+ System.out.println("Problem creating Robot. FAIL.");
+ throw new RuntimeException("Problem creating Robot. FAIL.");
+
+ }
+
+ robot.setAutoDelay(500);
+ robot.setAutoWaitForIdle(true);
+
+ // Show test Frame
+ Frame frame = new Frame("ScrollbarMouseWheelTest");
+ frame.addWindowListener(this);
+ pnl = new Panel();
+ pnl.setLayout(new GridLayout(1, 2));
+ pnl.addMouseWheelListener(this);
+ sb1 = new Scrollbar();
+ sb1.addMouseWheelListener(this);
+ pnl.add(sb1);
+ sb2 = new Scrollbar();
+ pnl.add(sb2);
+ frame.add(pnl);
+ frame.setSize(200, 400);
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ frame.toFront();
+
+ // When Frame is active, start testing (handled in windowActivated())
+ while (true) {
+ synchronized (sema) {
+ if (sema.getVal()) {
+ break;
+ }
+ }
+ }
+ // up on sb1
+ testComp(sb1, true);
+ // down on sb1
+ testComp(sb1, false);
+ // up on sb2
+ testComp(sb2, true);
+ // down on sb2
+ testComp(sb2, false);
+ frame.dispose();
+ System.out.println("Test done.");
+ if (sb1upevents == REPS &&
+ sb2upevents == 0 &&
+ pnlupevents == PANEL_REPS &&
+ sb1downevents == REPS &&
+ sb2downevents == 0 &&
+ pnldownevents == PANEL_REPS) {
+ System.out.println("PASSED.");
+ } else {
+ System.out.println("Test Failed:" +
+ "\n\tsb1upevents =" + sb1upevents +
+ "\n\tsb2upevents = " + sb2upevents +
+ "\n\tpnlupevents = " + pnlupevents +
+ "\n\tsb1downevents =" + sb1downevents +
+ "\n\tsb2downevents = " + sb2downevents +
+ "\n\tpnldownevents = " + pnldownevents);
+ throw new RuntimeException("Test FAILED.");
+ }
+ }
+
+ public void testComp(Component comp, boolean up) {
+ Point loc = comp.getLocationOnScreen();
+ robot.mouseMove(loc.x + comp.getWidth() / 2,
+ loc.y + comp.getHeight() / 2);
+ for (int loop = 0; loop < REPS; loop++) {
+ System.out.println("Robot.mouseWheel() on " + comp.getName());
+ robot.mouseWheel(up ? -1 : 1);
+ }
+ }
+
+ public void mouseWheelMoved(MouseWheelEvent mwe) {
+ Component src = mwe.getComponent();
+ System.out.println("mouseWheelMoved() on " + src.getName());
+ if (mwe.getWheelRotation() == -1) {
+ if (src == sb1) {
+ sb1upevents++;
+ } else if (src == sb2) {
+ sb2upevents++;
+ } else if (src == pnl) {
+ pnlupevents++;
+ } else {
+ System.out.println("weird source component");
+ }
+ } else if (mwe.getWheelRotation() == 1) {
+ if (src == sb1) {
+ sb1downevents++;
+ } else if (src == sb2) {
+ sb2downevents++;
+ } else if (src == pnl) {
+ pnldownevents++;
+ } else {
+ System.out.println("weird source component");
+ }
+ } else {
+ System.out.println("weird wheel rotation");
+ }
+ }
+
+ public void windowActivated(WindowEvent we) {
+ synchronized (sema) {
+ sema.setVal(true);
+ }
+ }
+
+ public void windowClosed(WindowEvent we) {}
+ public void windowClosing(WindowEvent we) {}
+ public void windowDeactivated(WindowEvent we) {}
+ public void windowDeiconified(WindowEvent we) {}
+ public void windowIconified(WindowEvent we) {}
+ public void windowOpened(WindowEvent we) {}
+}// class ScrollbarMouseWheelTest
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/font/OpenType/OpticalBoundsTagTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.awt.font.OpenType;
+import java.io.IOException;
+
+/**
+ * @test
+ * @bug 8077584
+ * @summary Test for TAG_OPBD tag. Should be unique and not same as TAG_MORT.
+ * @run main OpticalBoundsTagTest
+ */
+
+public class OpticalBoundsTagTest {
+
+ public static void main(String[] a) throws Exception {
+
+ int tag_opbd = java.awt.font.OpenType.TAG_OPBD;
+ if (tag_opbd == java.awt.font.OpenType.TAG_MORT) {
+ System.out.println("Test failed: TAG_OPBD:" + tag_opbd);
+ throw new RuntimeException("TAG_OPBD same as TAG_MORT");
+ } else {
+ System.out.println("Test passed: TAG_OPBD: " + tag_opbd);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/InputStream/ReadAllBytes.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Random;
+import jdk.testlibrary.RandomFactory;
+
+/*
+ * @test
+ * @bug 8080835
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.*
+ * @run main ReadAllBytes
+ * @summary Basic test for InputStream.readAllBytes
+ * @key randomness
+ */
+
+public class ReadAllBytes {
+
+ private static Random generator = RandomFactory.getRandom();
+
+ public static void main(String[] args) throws IOException {
+ test(new byte[]{});
+ test(new byte[]{1, 2, 3});
+ test(createRandomBytes(1024));
+ test(createRandomBytes((1 << 13) - 1));
+ test(createRandomBytes((1 << 13)));
+ test(createRandomBytes((1 << 13) + 1));
+ test(createRandomBytes((1 << 15) - 1));
+ test(createRandomBytes((1 << 15)));
+ test(createRandomBytes((1 << 15) + 1));
+ test(createRandomBytes((1 << 17) - 1));
+ test(createRandomBytes((1 << 17)));
+ test(createRandomBytes((1 << 17) + 1));
+ }
+
+ static void test(byte[] expectedBytes) throws IOException {
+ int expectedLength = expectedBytes.length;
+ WrapperInputStream in = new WrapperInputStream(new ByteArrayInputStream(expectedBytes));
+ byte[] readBytes = in.readAllBytes();
+
+ int x;
+ byte[] tmp = new byte[10];
+ check((x = in.read()) == -1,
+ "Expected end of stream from read(), got " + x);
+ check((x = in.read(tmp)) == -1,
+ "Expected end of stream from read(byte[]), got " + x);
+ check((x = in.read(tmp, 0, tmp.length)) == -1,
+ "Expected end of stream from read(byte[], int, int), got " + x);
+ check(in.readAllBytes().length == 0,
+ "Expected readAllBytes to return empty byte array");
+ check(expectedLength == readBytes.length,
+ "Expected length " + expectedLength + ", got " + readBytes.length);
+ check(Arrays.equals(expectedBytes, readBytes),
+ "Expected[" + expectedBytes + "], got:[" + readBytes + "]");
+ check(!in.isClosed(), "Stream unexpectedly closed");
+ }
+
+ static byte[] createRandomBytes(int size) {
+ byte[] bytes = new byte[size];
+ generator.nextBytes(bytes);
+ return bytes;
+ }
+
+ static void check(boolean cond, Object ... failedArgs) {
+ if (cond)
+ return;
+ StringBuilder sb = new StringBuilder();
+ for (Object o : failedArgs)
+ sb.append(o);
+ throw new RuntimeException(sb.toString());
+ }
+
+ static class WrapperInputStream extends FilterInputStream {
+ private boolean closed;
+ WrapperInputStream(InputStream in) { super(in); }
+ @Override public void close() throws IOException { closed = true; in.close(); }
+ boolean isClosed() { return closed; }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/InputStream/ReadNBytes.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Random;
+import jdk.testlibrary.RandomFactory;
+
+/*
+ * @test
+ * @bug 8080835
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.*
+ * @run main ReadNBytes
+ * @summary Basic test for InputStream.readNBytes
+ * @key randomness
+ */
+
+public class ReadNBytes {
+
+ private static Random generator = RandomFactory.getRandom();
+
+ public static void main(String[] args) throws IOException {
+ test(new byte[]{1, 2, 3});
+ test(createRandomBytes(1024));
+ test(createRandomBytes((1 << 13) - 1));
+ test(createRandomBytes((1 << 13)));
+ test(createRandomBytes((1 << 13) + 1));
+ test(createRandomBytes((1 << 15) - 1));
+ test(createRandomBytes((1 << 15)));
+ test(createRandomBytes((1 << 15) + 1));
+ test(createRandomBytes((1 << 17) - 1));
+ test(createRandomBytes((1 << 17)));
+ test(createRandomBytes((1 << 17) + 1));
+ }
+
+ static void test(byte[] inputBytes) throws IOException {
+ int length = inputBytes.length;
+ WrapperInputStream in = new WrapperInputStream(new ByteArrayInputStream(inputBytes));
+ byte[] readBytes = new byte[(length / 2) + 1];
+ int nread = in.readNBytes(readBytes, 0, readBytes.length);
+
+ int x;
+ byte[] tmp;
+ check(nread == readBytes.length,
+ "Expected number of bytes read: " + readBytes.length + ", got: " + nread);
+ check(Arrays.equals((tmp = Arrays.copyOf(inputBytes, nread)), readBytes),
+ "Expected[" + tmp + "], got:[" + readBytes + "]");
+ check(!in.isClosed(), "Stream unexpectedly closed");
+
+ // Read again
+ nread = in.readNBytes(readBytes, 0, readBytes.length);
+
+ check(nread == length - readBytes.length,
+ "Expected number of bytes read: " + (length - readBytes.length) + ", got: " + nread);
+ check(Arrays.equals((tmp = Arrays.copyOfRange(inputBytes, readBytes.length, length)),
+ Arrays.copyOf(readBytes, nread)),
+ "Expected[" + tmp + "], got:[" + readBytes + "]");
+ // Expect end of stream
+ check((x = in.read()) == -1,
+ "Expected end of stream from read(), got " + x);
+ check((x = in.read(tmp)) == -1,
+ "Expected end of stream from read(byte[]), got " + x);
+ check((x = in.read(tmp, 0, tmp.length)) == -1,
+ "Expected end of stream from read(byte[], int, int), got " + x);
+ check((x = in.readNBytes(tmp, 0, tmp.length)) == 0,
+ "Expected end of stream, 0, from readNBytes(byte[], int, int), got " + x);
+ check(!in.isClosed(), "Stream unexpectedly closed");
+ }
+
+ static byte[] createRandomBytes(int size) {
+ byte[] bytes = new byte[size];
+ generator.nextBytes(bytes);
+ return bytes;
+ }
+
+ static void check(boolean cond, Object ... failedArgs) {
+ if (cond)
+ return;
+ StringBuilder sb = new StringBuilder();
+ for (Object o : failedArgs)
+ sb.append(o);
+ throw new RuntimeException(sb.toString());
+ }
+
+
+ static class WrapperInputStream extends FilterInputStream {
+ private boolean closed;
+ WrapperInputStream(InputStream in) { super(in); }
+ @Override public void close() throws IOException { closed = true; in.close(); }
+ boolean isClosed() { return closed; }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/Serializable/failureAtomicity/Bar.template Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package $package;
+
+import java.io.IOException;
+import java.io.Serializable;
+import failureAtomicity.SerialRef;
+
+public class Bar extends Foo implements Serializable {
+ static final long serialVersionUID = -0L;
+
+ public final long barPrim;
+ public final String barRef;
+
+ public final SerialRef ref; // So we can retrieve a reference to check
+ public $zebra_type zebraBar; // ordered alphabetically, must be last
+
+ public Bar(int fooPrim, String fooRef, $foo_zebra_type fooZebra,
+ long barPrim, String barRef, $zebra_type zebra) {
+ super(fooPrim, fooRef, fooZebra);
+ this.barPrim = barPrim;
+ this.barRef = barRef;
+ this.zebraBar = zebra;
+ this.ref = new SerialRef(this);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("$package.Bar[")
+ .append("barPrim:").append(barPrim)
+ .append(", barRef:").append(barRef)
+ .append(", zebraBar:").append(zebraBar)
+ .append(", " + super.toString())
+ .toString();
+ }
+
+//$has_readObject private void readObject(java.io.ObjectInputStream in)
+//$has_readObject throws IOException, ClassNotFoundException
+//$has_readObject {
+//$has_readObject in.defaultReadObject();
+//$has_readObject }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/Serializable/failureAtomicity/FailureAtomicity.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8071474
+ * @summary Better failure atomicity for default read object.
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.FileUtils
+ * @compile FailureAtomicity.java SerialRef.java
+ * @run main failureAtomicity.FailureAtomicity
+ */
+
+package failureAtomicity;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.UncheckedIOException;
+import java.lang.reflect.Constructor;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import jdk.testlibrary.FileUtils;
+
+@SuppressWarnings("unchecked")
+public class FailureAtomicity {
+ static final Path TEST_SRC = Paths.get(System.getProperty("test.src", "."));
+ static final Path TEST_CLASSES = Paths.get(System.getProperty("test.classes", "."));
+ static final Path fooTemplate = TEST_SRC.resolve("Foo.template");
+ static final Path barTemplate = TEST_SRC.resolve("Bar.template");
+
+ static final String[] PKGS = { "a.b.c", "x.y.z" };
+
+ public static void main(String[] args) throws Exception {
+ test_Foo();
+ test_BadFoo(); // 'Bad' => incompatible type; cannot be "fully" deserialized
+ test_FooWithReadObject();
+ test_BadFooWithReadObject();
+
+ test_Foo_Bar();
+ test_Foo_BadBar();
+ test_BadFoo_Bar();
+ test_BadFoo_BadBar();
+ test_Foo_BarWithReadObject();
+ test_Foo_BadBarWithReadObject();
+ test_BadFoo_BarWithReadObject();
+ test_BadFoo_BadBarWithReadObject();
+ test_FooWithReadObject_Bar();
+ test_FooWithReadObject_BadBar();
+ test_BadFooWithReadObject_Bar();
+ test_BadFooWithReadObject_BadBar();
+ }
+
+ static final BiConsumer<Object,Object> FOO_FIELDS_EQUAL = (a,b) -> {
+ try {
+ int aPrim = a.getClass().getField("fooPrim").getInt(a);
+ int bPrim = b.getClass().getField("fooPrim").getInt(b);
+ if (aPrim != bPrim)
+ throw new AssertionError("Not equal: (" + aPrim + "!=" + bPrim
+ + "), in [" + a + "] [" + b + "]");
+ Object aRef = a.getClass().getField("fooRef").get(a);
+ Object bRef = b.getClass().getField("fooRef").get(b);
+ if (!aRef.equals(bRef))
+ throw new RuntimeException("Not equal: (" + aRef + "!=" + bRef
+ + "), in [" + a + "] [" + b + "]");
+ } catch (NoSuchFieldException | IllegalAccessException x) {
+ throw new InternalError(x);
+ }
+ };
+ static final BiConsumer<Object,Object> FOO_FIELDS_DEFAULT = (ignore,b) -> {
+ try {
+ int aPrim = b.getClass().getField("fooPrim").getInt(b);
+ if (aPrim != 0)
+ throw new AssertionError("Expected 0, got:" + aPrim
+ + ", in [" + b + "]");
+ Object aRef = b.getClass().getField("fooRef").get(b);
+ if (aRef != null)
+ throw new RuntimeException("Expected null, got:" + aRef
+ + ", in [" + b + "]");
+ } catch (NoSuchFieldException | IllegalAccessException x) {
+ throw new InternalError(x);
+ }
+ };
+ static final BiConsumer<Object,Object> BAR_FIELDS_EQUAL = (a,b) -> {
+ try {
+ long aPrim = a.getClass().getField("barPrim").getLong(a);
+ long bPrim = b.getClass().getField("barPrim").getLong(b);
+ if (aPrim != bPrim)
+ throw new AssertionError("Not equal: (" + aPrim + "!=" + bPrim
+ + "), in [" + a + "] [" + b + "]");
+ Object aRef = a.getClass().getField("barRef").get(a);
+ Object bRef = b.getClass().getField("barRef").get(b);
+ if (!aRef.equals(bRef))
+ throw new RuntimeException("Not equal: (" + aRef + "!=" + bRef
+ + "), in [" + a + "] [" + b + "]");
+ } catch (NoSuchFieldException | IllegalAccessException x) {
+ throw new InternalError(x);
+ }
+ };
+ static final BiConsumer<Object,Object> BAR_FIELDS_DEFAULT = (ignore,b) -> {
+ try {
+ long aPrim = b.getClass().getField("barPrim").getLong(b);
+ if (aPrim != 0L)
+ throw new AssertionError("Expected 0, got:" + aPrim
+ + ", in [" + b + "]");
+ Object aRef = b.getClass().getField("barRef").get(b);
+ if (aRef != null)
+ throw new RuntimeException("Expected null, got:" + aRef
+ + ", in [" + b + "]");
+ } catch (NoSuchFieldException | IllegalAccessException x) {
+ throw new InternalError(x);
+ }
+ };
+
+ static void test_Foo() {
+ testFoo("Foo", "String", false, false, FOO_FIELDS_EQUAL); }
+ static void test_BadFoo() {
+ testFoo("BadFoo", "byte[]", true, false, FOO_FIELDS_DEFAULT); }
+ static void test_FooWithReadObject() {
+ testFoo("FooWithReadObject", "String", false, true, FOO_FIELDS_EQUAL); }
+ static void test_BadFooWithReadObject() {
+ testFoo("BadFooWithReadObject", "byte[]", true, true, FOO_FIELDS_DEFAULT); }
+
+ static void testFoo(String testName, String xyzZebraType,
+ boolean expectCCE, boolean withReadObject,
+ BiConsumer<Object,Object>... resultCheckers) {
+ System.out.println("\nTesting " + testName);
+ try {
+ Path testRoot = testDir(testName);
+ Path srcRoot = Files.createDirectory(testRoot.resolve("src"));
+ List<Path> srcFiles = new ArrayList<>();
+ srcFiles.add(createSrc(PKGS[0], fooTemplate, srcRoot, "String", withReadObject));
+ srcFiles.add(createSrc(PKGS[1], fooTemplate, srcRoot, xyzZebraType, withReadObject));
+
+ Path build = Files.createDirectory(testRoot.resolve("build"));
+ javac(build, srcFiles);
+
+ URLClassLoader loader = new URLClassLoader(new URL[]{ build.toUri().toURL() },
+ FailureAtomicity.class.getClassLoader());
+ Class<?> fooClass = Class.forName(PKGS[0] + ".Foo", true, loader);
+ Constructor<?> ctr = fooClass.getConstructor(
+ new Class<?>[]{int.class, String.class, String.class});
+ Object abcFoo = ctr.newInstance(5, "chegar", "zebra");
+
+ try {
+ toOtherPkgInstance(abcFoo, loader);
+ if (expectCCE)
+ throw new AssertionError("Expected CCE not thrown");
+ } catch (ClassCastException e) {
+ if (!expectCCE)
+ throw new AssertionError("UnExpected CCE: " + e);
+ }
+
+ Object deserialInstance = failureAtomicity.SerialRef.obj;
+
+ System.out.println("abcFoo: " + abcFoo);
+ System.out.println("deserialInstance: " + deserialInstance);
+
+ for (BiConsumer<Object, Object> rc : resultCheckers)
+ rc.accept(abcFoo, deserialInstance);
+ } catch (IOException x) {
+ throw new UncheckedIOException(x);
+ } catch (ReflectiveOperationException x) {
+ throw new InternalError(x);
+ }
+ }
+
+ static void test_Foo_Bar() {
+ testFooBar("Foo_Bar", "String", "String", false, false, false,
+ FOO_FIELDS_EQUAL, BAR_FIELDS_EQUAL);
+ }
+ static void test_Foo_BadBar() {
+ testFooBar("Foo_BadBar", "String", "byte[]", true, false, false,
+ FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT);
+ }
+ static void test_BadFoo_Bar() {
+ testFooBar("BadFoo_Bar", "byte[]", "String", true, false, false,
+ FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT);
+ }
+ static void test_BadFoo_BadBar() {
+ testFooBar("BadFoo_BadBar", "byte[]", "byte[]", true, false, false,
+ FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT);
+ }
+ static void test_Foo_BarWithReadObject() {
+ testFooBar("Foo_BarWithReadObject", "String", "String", false, false, true,
+ FOO_FIELDS_EQUAL, BAR_FIELDS_EQUAL);
+ }
+ static void test_Foo_BadBarWithReadObject() {
+ testFooBar("Foo_BadBarWithReadObject", "String", "byte[]", true, false, true,
+ FOO_FIELDS_EQUAL, BAR_FIELDS_DEFAULT);
+ }
+ static void test_BadFoo_BarWithReadObject() {
+ testFooBar("BadFoo_BarWithReadObject", "byte[]", "String", true, false, true,
+ FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT);
+ }
+ static void test_BadFoo_BadBarWithReadObject() {
+ testFooBar("BadFoo_BadBarWithReadObject", "byte[]", "byte[]", true, false, true,
+ FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT);
+ }
+
+ static void test_FooWithReadObject_Bar() {
+ testFooBar("FooWithReadObject_Bar", "String", "String", false, true, false,
+ FOO_FIELDS_EQUAL, BAR_FIELDS_EQUAL);
+ }
+ static void test_FooWithReadObject_BadBar() {
+ testFooBar("FooWithReadObject_BadBar", "String", "byte[]", true, true, false,
+ FOO_FIELDS_EQUAL, BAR_FIELDS_DEFAULT);
+ }
+ static void test_BadFooWithReadObject_Bar() {
+ testFooBar("BadFooWithReadObject_Bar", "byte[]", "String", true, true, false,
+ FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT);
+ }
+ static void test_BadFooWithReadObject_BadBar() {
+ testFooBar("BadFooWithReadObject_BadBar", "byte[]", "byte[]", true, true, false,
+ FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT);
+ }
+
+ static void testFooBar(String testName, String xyzFooZebraType,
+ String xyzBarZebraType, boolean expectCCE,
+ boolean fooWithReadObject, boolean barWithReadObject,
+ BiConsumer<Object,Object>... resultCheckers) {
+ System.out.println("\nTesting " + testName);
+ try {
+ Path testRoot = testDir(testName);
+ Path srcRoot = Files.createDirectory(testRoot.resolve("src"));
+ List<Path> srcFiles = new ArrayList<>();
+ srcFiles.add(createSrc(PKGS[0], fooTemplate, srcRoot, "String",
+ fooWithReadObject, "String"));
+ srcFiles.add(createSrc(PKGS[1], fooTemplate, srcRoot, xyzFooZebraType,
+ fooWithReadObject, xyzFooZebraType));
+ srcFiles.add(createSrc(PKGS[0], barTemplate, srcRoot, "String",
+ barWithReadObject, "String"));
+ srcFiles.add(createSrc(PKGS[1], barTemplate, srcRoot, xyzBarZebraType,
+ barWithReadObject, xyzFooZebraType));
+
+ Path build = Files.createDirectory(testRoot.resolve("build"));
+ javac(build, srcFiles);
+
+ URLClassLoader loader = new URLClassLoader(new URL[]{ build.toUri().toURL() },
+ FailureAtomicity.class.getClassLoader());
+ Class<?> fooClass = Class.forName(PKGS[0] + ".Bar", true, loader);
+ Constructor<?> ctr = fooClass.getConstructor(
+ new Class<?>[]{int.class, String.class, String.class,
+ long.class, String.class, String.class});
+ Object abcBar = ctr.newInstance( 5, "chegar", "zebraFoo", 111L, "aBar", "zebraBar");
+
+ try {
+ toOtherPkgInstance(abcBar, loader);
+ if (expectCCE)
+ throw new AssertionError("Expected CCE not thrown");
+ } catch (ClassCastException e) {
+ if (!expectCCE)
+ throw new AssertionError("UnExpected CCE: " + e);
+ }
+
+ Object deserialInstance = failureAtomicity.SerialRef.obj;
+
+ System.out.println("abcBar: " + abcBar);
+ System.out.println("deserialInstance: " + deserialInstance);
+
+ for (BiConsumer<Object, Object> rc : resultCheckers)
+ rc.accept(abcBar, deserialInstance);
+ } catch (IOException x) {
+ throw new UncheckedIOException(x);
+ } catch (ReflectiveOperationException x) {
+ throw new InternalError(x);
+ }
+ }
+
+ static Path testDir(String name) throws IOException {
+ Path testRoot = Paths.get("FailureAtomicity-" + name);
+ if (Files.exists(testRoot))
+ FileUtils.deleteFileTreeWithRetry(testRoot);
+ Files.createDirectory(testRoot);
+ return testRoot;
+ }
+
+ static String platformPath(String p) { return p.replace("/", File.separator); }
+ static String binaryName(String name) { return name.replace(".", "/"); }
+ static String condRemove(String line, String pattern, boolean hasReadObject) {
+ if (hasReadObject) { return line.replaceAll(pattern, ""); }
+ else { return line; }
+ }
+ static String condReplace(String line, String... zebraFooType) {
+ if (zebraFooType.length == 1) {
+ return line.replaceAll("\\$foo_zebra_type", zebraFooType[0]);
+ } else { return line; }
+ }
+ static String nameFromTemplate(Path template) {
+ return template.getFileName().toString().replaceAll(".template", "");
+ }
+
+ static Path createSrc(String pkg, Path srcTemplate, Path srcRoot,
+ String zebraType, boolean hasReadObject,
+ String... zebraFooType)
+ throws IOException
+ {
+ Path srcDst = srcRoot.resolve(platformPath(binaryName(pkg)));
+ Files.createDirectories(srcDst);
+ Path srcFile = srcDst.resolve(nameFromTemplate(srcTemplate) + ".java");
+
+ List<String> lines = Files.lines(srcTemplate)
+ .map(s -> s.replaceAll("\\$package", pkg))
+ .map(s -> s.replaceAll("\\$zebra_type", zebraType))
+ .map(s -> condReplace(s, zebraFooType))
+ .map(s -> condRemove(s, "//\\$has_readObject", hasReadObject))
+ .collect(Collectors.toList());
+ Files.write(srcFile, lines);
+ return srcFile;
+ }
+
+ static void javac(Path dest, List<Path> sourceFiles) throws IOException {
+ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+ try (StandardJavaFileManager fileManager =
+ compiler.getStandardFileManager(null, null, null)) {
+ List<File> files = sourceFiles.stream()
+ .map(p -> p.toFile())
+ .collect(Collectors.toList());
+ Iterable<? extends JavaFileObject> compilationUnits =
+ fileManager.getJavaFileObjectsFromFiles(files);
+ fileManager.setLocation(StandardLocation.CLASS_OUTPUT,
+ Arrays.asList(dest.toFile()));
+ fileManager.setLocation(StandardLocation.CLASS_PATH,
+ Arrays.asList(TEST_CLASSES.toFile()));
+ JavaCompiler.CompilationTask task = compiler
+ .getTask(null, fileManager, null, null, null, compilationUnits);
+ boolean passed = task.call();
+ if (!passed)
+ throw new RuntimeException("Error compiling " + files);
+ }
+ }
+
+ static Object toOtherPkgInstance(Object obj, ClassLoader loader)
+ throws IOException, ClassNotFoundException
+ {
+ byte[] bytes = serialize(obj);
+ bytes = replacePkg(bytes);
+ return deserialize(bytes, loader);
+ }
+
+ @SuppressWarnings("deprecation")
+ static byte[] replacePkg(byte[] bytes) {
+ String str = new String(bytes, 0);
+ str = str.replaceAll(PKGS[0], PKGS[1]);
+ str.getBytes(0, bytes.length, bytes, 0);
+ return bytes;
+ }
+
+ static byte[] serialize(Object obj) throws IOException {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream out = new ObjectOutputStream(baos);) {
+ out.writeObject(obj);
+ return baos.toByteArray();
+ }
+ }
+
+ static Object deserialize(byte[] data, ClassLoader l)
+ throws IOException, ClassNotFoundException
+ {
+ return new WithLoaderObjectInputStream(new ByteArrayInputStream(data), l)
+ .readObject();
+ }
+
+ static class WithLoaderObjectInputStream extends ObjectInputStream {
+ final ClassLoader loader;
+ WithLoaderObjectInputStream(InputStream is, ClassLoader loader)
+ throws IOException
+ {
+ super(is);
+ this.loader = loader;
+ }
+ @Override
+ protected Class<?> resolveClass(ObjectStreamClass desc)
+ throws IOException, ClassNotFoundException {
+ try {
+ return super.resolveClass(desc);
+ } catch (ClassNotFoundException x) {
+ String name = desc.getName();
+ return Class.forName(name, false, loader);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/Serializable/failureAtomicity/Foo.template Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package $package;
+
+import java.io.IOException;
+import java.io.Serializable;
+import failureAtomicity.SerialRef;
+
+public class Foo implements Serializable {
+ static final long serialVersionUID = -0L;
+
+ public final int fooPrim;
+ public final String fooRef;
+
+ public final SerialRef ref; // So we can retrieve a reference to check
+ public $zebra_type zebraFoo; // ordered alphabetically, must be last
+
+ public Foo(int fooPrim, String fooRef, $zebra_type zebra) {
+ this.fooPrim = fooPrim;
+ this.fooRef = fooRef;
+ this.zebraFoo = zebra;
+ this.ref = new SerialRef(this);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("$package.Foo[")
+ .append("fooPrim:").append(fooPrim)
+ .append(", fooRef:").append(fooRef)
+ .append(", zebraFoo:").append(zebraFoo).append("]")
+ .toString();
+ }
+
+//$has_readObject private void readObject(java.io.ObjectInputStream in)
+//$has_readObject throws IOException, ClassNotFoundException
+//$has_readObject {
+//$has_readObject in.defaultReadObject();
+//$has_readObject }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/Serializable/failureAtomicity/SerialRef.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package failureAtomicity;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+
+// For verification purposes only.
+
+public class SerialRef implements Serializable {
+ static final long serialVersionUID = -0L;
+ public static Object obj;
+
+ private final Object ref;
+
+ public SerialRef(Object ref) {
+ this.ref = ref;
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ SerialRef.obj = ref;
+ }
+}
--- a/jdk/test/java/lang/Character/UnicodeBlock/NonOptimalMapSize.java Thu Jun 04 09:31:49 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-
-/**
- * @test
- * @bug 8080535
- * @summary Expected size of Character.UnicodeBlock.map is not optimal
- */
-
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.Map;
-
-public class NonOptimalMapSize {
- public static void main(String[] args) throws Throwable {
- Class<?> ubCls = Character.UnicodeBlock.class;
- Field mapField = ubCls.getDeclaredField("map");
- mapField.setAccessible(true);
- Map<?,?> map = (Map<?,?>)mapField.get(null);
- if (!map.getClass().equals(HashMap.class)) {
- throw new RuntimeException(
- "Character.UnicodeBlock.map is expected to be HashMap");
- }
- int mapSize = map.size();
-
- Field sizeField = ubCls.getDeclaredField("INITIAL_CAPACITY");
- sizeField.setAccessible(true);
- int INITIAL_CAPACITY = sizeField.getInt(null);
-
- // Construct a HashMap with specified initial capacity
- HashMap<Object,Object> map1 = new HashMap<>(INITIAL_CAPACITY);
- Class<?> hmCls = HashMap.class;
- Field tableField = hmCls.getDeclaredField("table");
- tableField.setAccessible(true);
- // ... and fill it up
- map1.put(new Object(), new Object());
- final Object initialTable = tableField.get(map1);
- while (map1.size() < map.size() &&
- initialTable == tableField.get(map1)) {
- map1.put(new Object(), new Object());
- }
-
- // Now check that internal storage didn't change
- if (initialTable != tableField.get(map1)) {
- throw new RuntimeException(
- "Initial capacity " + INITIAL_CAPACITY +
- " was only enough to hold " + (map1.size()-1) +
- " entries, but needed " + map.size());
- }
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/Character/UnicodeBlock/OptimalMapSize.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8080535
+ * @summary Expected size of Character.UnicodeBlock.map is not optimal
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.OptimalCapacity
+ * @run main OptimalMapSize
+ */
+
+import jdk.testlibrary.OptimalCapacity;
+
+// What will be the number of the Unicode blocks in the future.
+//
+// According to http://www.unicode.org/versions/Unicode7.0.0/ ,
+// in Unicode 7 there will be added 32 new blocks (96 with aliases).
+// According to http://www.unicode.org/versions/beta-8.0.0.html ,
+// in Unicode 8 there will be added 10 more blocks (30 with aliases).
+//
+// After implementing support of Unicode 7 and 8 in Java, there will
+// be 510+96+30 = 636 entries in Character.UnicodeBlock.map.
+//
+// Initialization of the map and this test will have to be adjusted
+// accordingly then.
+
+public class OptimalMapSize {
+ public static void main(String[] args) throws Throwable {
+ // The initial size of Character.UnicodeBlock.map.
+ // See src/java.base/share/classes/java/lang/Character.java
+ int initialCapacity = (int)(510 / 0.75f + 1.0f);
+
+ OptimalCapacity.ofHashMap(Character.UnicodeBlock.class,
+ "map", initialCapacity);
+ }
+}
--- a/jdk/test/java/lang/ProcessBuilder/Basic.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/java/lang/ProcessBuilder/Basic.java Thu Jun 04 18:49:37 2015 -0700
@@ -36,6 +36,7 @@
*/
import java.lang.ProcessBuilder.Redirect;
+import java.lang.ProcessHandle;
import static java.lang.ProcessBuilder.Redirect.*;
import java.io.*;
@@ -47,7 +48,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.security.*;
-import sun.misc.Unsafe;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import static java.lang.System.getenv;
@@ -309,6 +309,8 @@
String action = args[0];
if (action.equals("sleep")) {
Thread.sleep(10 * 60 * 1000L);
+ } else if (action.equals("pid")) {
+ System.out.println(ProcessHandle.current().getPid());
} else if (action.equals("testIO")) {
String expected = "standard input";
char[] buf = new char[expected.length()+1];
@@ -1139,49 +1141,29 @@
}
static void checkProcessPid() {
- long actualPid = 0;
- long expectedPid = -1;
- if (Windows.is()) {
- String[] argsTasklist = {"tasklist.exe", "/NH", "/FI", "\"IMAGENAME eq tasklist.exe\""};
- ProcessBuilder pb = new ProcessBuilder(argsTasklist);
- try {
- Process proc = pb.start();
- expectedPid = proc.getPid();
-
- String output = commandOutput(proc);
- String[] splits = output.split("\\s+");
- actualPid = Integer.valueOf(splits[2]);
- } catch (Throwable t) {
- unexpected(t);
- }
- } else if (Unix.is() || MacOSX.is()) {
- String[] shArgs = {"sh", "-c", "echo $$"};
- ProcessBuilder pb = new ProcessBuilder(shArgs);
- try {
- Process proc = pb.start();
- expectedPid = proc.getPid();
-
- String output = commandOutput(proc);
- String[] splits = output.split("\\s+");
- actualPid = Integer.valueOf(splits[0]);
- } catch (Throwable t) {
- unexpected(t);
- }
- } else {
- fail("No test for checkProcessPid on platform: " + System.getProperty("os.name"));
- return;
+ ProcessBuilder pb = new ProcessBuilder();
+ List<String> list = new ArrayList<String>(javaChildArgs);
+ list.add("pid");
+ pb.command(list);
+ try {
+ Process p = pb.start();
+ String s = commandOutput(p);
+ long actualPid = Long.valueOf(s.trim());
+ long expectedPid = p.getPid();
+ equal(actualPid, expectedPid);
+ } catch (Throwable t) {
+ unexpected(t);
}
- equal(actualPid, expectedPid);
// Test the default implementation of Process.getPid
- try {
- DelegatingProcess p = new DelegatingProcess(null);
- p.getPid();
- fail("non-overridden Process.getPid method should throw UOE");
- } catch (UnsupportedOperationException uoe) {
- // correct
- }
+ DelegatingProcess p = new DelegatingProcess(null);
+ THROWS(UnsupportedOperationException.class,
+ () -> p.getPid(),
+ () -> p.toHandle(),
+ () -> p.supportsNormalTermination(),
+ () -> p.children(),
+ () -> p.allChildren());
}
@@ -2604,7 +2586,7 @@
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void fail() {failed++; Thread.dumpStack();}
- static void fail(String msg) {System.out.println(msg); fail();}
+ static void fail(String msg) {System.err.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void check(boolean cond) {if (cond) pass(); else fail();}
static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/Basic.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.util.Optional;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.testng.TestNG;
+import org.testng.annotations.Test;
+
+/*
+ * @test
+ * @summary Basic tests for ProcessHandler
+ * @author Roger Riggs
+ */
+public class Basic {
+ /**
+ * Tests of ProcessHandle.current.
+ */
+ @Test
+ public static void test1() {
+ try {
+ ProcessHandle self = ProcessHandle.current();
+ ProcessHandle self1 = ProcessHandle.current();
+ assertEquals(self, self1); //, "get pid twice should be same %d: %d");
+ } finally {
+ // Cleanup any left over processes
+ ProcessHandle.current().children().forEach(ProcessHandle::destroy);
+ }
+ }
+
+ /**
+ * Tests of ProcessHandle.get.
+ */
+ @Test
+ public static void test2() {
+ try {
+ ProcessHandle self = ProcessHandle.current();
+ long pid = self.getPid(); // known native process id
+ Optional<ProcessHandle> self1 = ProcessHandle.of(pid);
+ assertEquals(self1.get(), self,
+ "ProcessHandle.of(x.getPid()) should be equal getPid() %d: %d");
+
+ Optional<ProcessHandle> ph = ProcessHandle.of(pid);
+ assertEquals(pid, ph.get().getPid());
+ } finally {
+ // Cleanup any left over processes
+ ProcessHandle.current().children().forEach(ProcessHandle::destroy);
+ }
+ }
+
+ @Test
+ public static void test3() {
+ // Test can get parent of current
+ ProcessHandle ph = ProcessHandle.current();
+ try {
+ Optional<ProcessHandle> pph = ph.parent();
+ assertTrue(pph.isPresent(), "Current has a Parent");
+ } finally {
+ // Cleanup any left over processes
+ ProcessHandle.current().children().forEach(ProcessHandle::destroy);
+ }
+ }
+
+ @Test
+ public static void test4() {
+ try {
+ Process p = new ProcessBuilder("sleep", "0").start();
+ p.waitFor();
+
+ long deadPid = p.getPid();
+ p = null; // Forget the process
+
+ Optional<ProcessHandle> t = ProcessHandle.of(deadPid);
+ assertFalse(t.isPresent(), "Handle created for invalid pid:" + t);
+ } catch (IOException | InterruptedException ex) {
+ fail("Unexpected exception", ex);
+ } finally {
+ // Cleanup any left over processes
+ ProcessHandle.current().children().forEach(ProcessHandle::destroy);
+ }
+ }
+
+ @Test
+ public static void test5() {
+ // Always contains itself.
+ ProcessHandle current = ProcessHandle.current();
+ List<ProcessHandle> list = ProcessHandle.allProcesses().collect(Collectors.toList());
+ if (!list.stream()
+ .anyMatch(ph -> ph.equals(ProcessHandle.current()))) {
+ System.out.printf("current: %s%n", current);
+ System.out.printf("all processes.size: %d%n", list.size());
+ list.forEach(p -> ProcessUtil.printProcess(p, " allProcesses: "));
+ fail("current process not found in all processes");
+ }
+ }
+
+ @Test(expectedExceptions = IllegalStateException.class)
+ public static void test6() {
+ ProcessHandle.current().onExit();
+ }
+
+ @Test(expectedExceptions = IllegalStateException.class)
+ public static void test7() {
+ ProcessHandle.current().destroyForcibly();
+ }
+
+ // Main can be used to run the tests from the command line with only testng.jar.
+ @SuppressWarnings("raw_types")
+ public static void main(String[] args) {
+ Class<?>[] testclass = {TreeTest.class};
+ TestNG testng = new TestNG();
+ testng.setTestClasses(testclass);
+ testng.run();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/InfoTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.lang.ProcessBuilder;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.UserPrincipal;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Random;
+import java.util.Scanner;
+import java.util.StringTokenizer;
+import java.util.concurrent.TimeUnit;
+
+import jdk.testlibrary.Platform;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import org.testng.TestNG;
+
+/*
+ * @test
+ * @library /lib/testlibrary
+ * @summary Functions of ProcessHandle.Info
+ * @author Roger Riggs
+ */
+
+public class InfoTest {
+
+ static String whoami;
+
+ static {
+ try {
+ // Create a file and take the username from the file
+ Path p = Paths.get("OwnerName.tmp");
+ Files.createFile(p);
+ UserPrincipal owner = Files.getOwner(p);
+ whoami = owner.getName();
+ Files.delete(p);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ throw new UncheckedIOException("tmp file", ex);
+ }
+ }
+
+ // Main can be used to run the tests from the command line with only testng.jar.
+ @SuppressWarnings("raw_types")
+ public static void main(String[] args) {
+ Class<?>[] testclass = {InfoTest.class};
+ TestNG testng = new TestNG();
+ testng.setTestClasses(testclass);
+ testng.run();
+ }
+
+ /**
+ * Test that cputime used shows up in ProcessHandle.info
+ */
+ @Test
+ public static void test1() {
+ System.out.println("Note: when run in samevm mode the cputime of the " +
+ "test runner is included.");
+ ProcessHandle self = ProcessHandle.current();
+
+ Duration somecpu = Duration.ofMillis(200L);
+ Instant end = Instant.now().plus(somecpu);
+ while (Instant.now().isBefore(end)) {
+ // waste the cpu
+ }
+ ProcessHandle.Info info = self.info();
+ System.out.printf(" info: %s%n", info);
+ Optional<Duration> totalCpu = info.totalCpuDuration();
+ if (totalCpu.isPresent() && (totalCpu.get().compareTo(somecpu) < 0)) {
+ Assert.fail("reported cputime less than expected: " + somecpu + ", " +
+ "actual: " + info.totalCpuDuration());
+ }
+ }
+
+ /**
+ * Spawn a child with arguments and check they are visible via the ProcessHandle.
+ */
+ @Test
+ public static void test2() {
+ try {
+ long cpulooptime = 1 << 8;
+ String[] extraArgs = {"pid", "parent", "stdin"};
+ Instant beforeStart = Instant.now().truncatedTo(ChronoUnit.SECONDS);
+ JavaChild p1 = JavaChild.spawnJavaChild((Object[])extraArgs);
+ Instant afterStart = Instant.now();
+
+ try (BufferedReader lines = p1.outputReader()) {
+ Duration lastCpu = Duration.ofMillis(0L);
+ for (int j = 0; j < 20; j++) {
+
+ p1.sendAction("cpuloop", cpulooptime);
+ p1.sendAction("cputime", "");
+
+ // Read cputime from child
+ Duration childCpuTime = null;
+ // Read lines from the child until the result from cputime is returned
+ String s;
+ while ((s = lines.readLine()) != null) {
+ String[] split = s.trim().split(" ");
+ if (split.length == 3 && split[1].equals("cputime")) {
+ long nanos = Long.valueOf(split[2]);
+ childCpuTime = Duration.ofNanos(nanos);
+ break; // found the result we're looking for
+ }
+ }
+
+
+ ProcessHandle.Info info = p1.info();
+ System.out.printf(" info: %s%n", info);
+
+ if (info.user().isPresent()) {
+ String user = info.user().get();
+ Assert.assertNotNull(user, "User name");
+ Assert.assertEquals(user, whoami, "User name");
+ }
+
+ Optional<String> command = info.command();
+ if (command.isPresent()) {
+ String javaExe = System.getProperty("test.jdk") +
+ File.separator + "bin" + File.separator + "java";
+ String expected = Platform.isWindows() ? javaExe + ".exe" : javaExe;
+ Path expectedPath = Paths.get(expected);
+ Path actualPath = Paths.get(command.get());
+ Assert.assertTrue(Files.isSameFile(expectedPath, actualPath),
+ "Command: expected: " + javaExe + ", actual: " + command.get());
+ }
+
+ if (info.arguments().isPresent()) {
+ String[] args = info.arguments().get();
+
+ if (Platform.isLinux() || Platform.isOSX()) {
+ int offset = args.length - extraArgs.length;
+ for (int i = 0; i < extraArgs.length; i++) {
+ Assert.assertEquals(args[offset + i], extraArgs[i],
+ "Actual argument mismatch, index: " + i);
+ }
+ } else if (Platform.isSolaris()) {
+ Assert.assertEquals(args.length, 1,
+ "Expected argument list length: 1");
+ Assert.assertNotNull(args[0],
+ "Expected an argument");
+ } else {
+ System.out.printf("No argument test for OS: %s%n", Platform.getOsName());
+ }
+
+ // Now check that the first argument is not the same as the executed command
+ if (args.length > 0) {
+ Assert.assertNotEquals(args[0], command,
+ "First argument should not be the executable: args[0]: "
+ + args[0] + ", command: " + command);
+ }
+ }
+
+ if (info.totalCpuDuration().isPresent()) {
+ Duration totalCPU = info.totalCpuDuration().get();
+ Duration epsilon = Duration.ofMillis(200L);
+ Assert.assertTrue(totalCPU.toNanos() > 0L,
+ "total cpu time expected > 0ms, actual: " + totalCPU);
+ Assert.assertTrue(totalCPU.toNanos() < lastCpu.toNanos() + 10_000_000_000L,
+ "total cpu time expected < 10s more than previous iteration, actual: " + totalCPU);
+ if (childCpuTime != null) {
+ System.out.printf(" info.totalCPU: %s, childCpuTime: %s, diff: %s%n",
+ totalCPU.toNanos(), childCpuTime.toNanos(), childCpuTime.toNanos() - totalCPU.toNanos());
+ Assert.assertTrue(checkEpsilon(childCpuTime, totalCPU, epsilon),
+ childCpuTime + " should be within " +
+ epsilon + " of " + totalCPU);
+ }
+ lastCpu = totalCPU;
+ }
+
+ if (info.startInstant().isPresent()) {
+ Instant startTime = info.startInstant().get();
+ Assert.assertTrue(startTime.isBefore(afterStart),
+ "startTime after process spawn completed"
+ + startTime + " + > " + afterStart);
+ }
+ }
+ }
+ p1.waitFor(5, TimeUnit.SECONDS);
+ } catch (IOException | InterruptedException ie) {
+ ie.printStackTrace(System.out);
+ Assert.fail("unexpected exception", ie);
+ }
+ }
+
+ /**
+ * Spawn a child with arguments and check they are visible via the ProcessHandle.
+ */
+ @Test
+ public static void test3() {
+ try {
+ for (int sleepTime : Arrays.asList(1, 2)) {
+ Process p = spawn("sleep", String.valueOf(sleepTime));
+ ProcessHandle.Info info = p.info();
+ System.out.printf(" info: %s%n", info);
+
+ if (info.user().isPresent()) {
+ String user = info.user().get();
+ Assert.assertNotNull(user);
+ Assert.assertEquals(user, whoami);
+ }
+ if (info.command().isPresent()) {
+ String command = info.command().get();
+ String expected = Platform.isWindows() ? "sleep.exe" : "sleep";
+ Assert.assertTrue(command.endsWith(expected), "Command: expected: \'" +
+ expected + "\', actual: " + command);
+
+ // Verify the command exists and is executable
+ File exe = new File(command);
+ Assert.assertTrue(exe.exists(), "command must exist: " + exe);
+ Assert.assertTrue(exe.canExecute(), "command must be executable: " + exe);
+ }
+ if (info.arguments().isPresent()) {
+ String[] args = info.arguments().get();
+ if (args.length > 0) {
+ Assert.assertEquals(args[0], String.valueOf(sleepTime));
+ }
+ }
+ Assert.assertTrue(p.waitFor(15, TimeUnit.SECONDS));
+ }
+ } catch (IOException | InterruptedException ex) {
+ ex.printStackTrace(System.out);;
+ } finally {
+ // Destroy any children that still exist
+ ProcessUtil.destroyProcessTree(ProcessHandle.current());
+ }
+ }
+
+ /**
+ * Cross check the cputime reported from java.management with that for the current process.
+ */
+ @Test
+ public static void test4() {
+ Duration myCputime1 = ProcessUtil.MXBeanCpuTime();
+
+ Optional<Duration> dur1 = ProcessHandle.current().info().totalCpuDuration();
+
+ Duration myCputime2 = ProcessUtil.MXBeanCpuTime();
+
+ Optional<Duration> dur2 = ProcessHandle.current().info().totalCpuDuration();
+
+ if (dur1.isPresent() && dur2.isPresent()) {
+ Duration total1 = dur1.get();
+ Duration total2 = dur2.get(); ;
+ System.out.printf(" total1 vs. mbean: %s, getProcessCpuTime: %s, diff: %s%n",
+ Objects.toString(total1), myCputime1, myCputime1.minus(total1));
+ System.out.printf(" total2 vs. mbean: %s, getProcessCpuTime: %s, diff: %s%n",
+ Objects.toString(total2), myCputime2, myCputime2.minus(total2));
+
+ Duration epsilon = Duration.ofMillis(200L); // Epsilon is 200ms.
+ Assert.assertTrue(checkEpsilon(myCputime1, myCputime2, epsilon),
+ myCputime1.toNanos() + " should be within " + epsilon
+ + " of " + myCputime2.toNanos());
+ Assert.assertTrue(checkEpsilon(total1, total2, epsilon),
+ total1.toNanos() + " should be within " + epsilon
+ + " of " + total2.toNanos());
+ Assert.assertTrue(checkEpsilon(myCputime1, total1, epsilon),
+ myCputime1.toNanos() + " should be within " + epsilon
+ + " of " + total1.toNanos());
+ Assert.assertTrue(checkEpsilon(total1, myCputime2, epsilon),
+ total1.toNanos() + " should be within " + epsilon
+ + " of " + myCputime2.toNanos());
+ Assert.assertTrue(checkEpsilon(myCputime2, total2, epsilon),
+ myCputime2.toNanos() + " should be within " + epsilon
+ + " of " + total2.toNanos());
+ }
+ }
+
+ @Test
+ public static void test5() {
+ ProcessHandle self = ProcessHandle.current();
+ Random r = new Random();
+ for (int i = 0; i < 30; i++) {
+ Instant end = Instant.now().plusMillis(500L);
+ while (end.isBefore(Instant.now())) {
+ // burn the cpu time checking the time
+ long x = r.nextLong();
+ }
+ if (self.info().totalCpuDuration().isPresent()) {
+ Duration totalCpu = self.info().totalCpuDuration().get();
+ long infoTotalCputime = totalCpu.toNanos();
+ long beanCputime = ProcessUtil.MXBeanCpuTime().toNanos();
+ System.out.printf(" infoTotal: %12d, beanCpu: %12d, diff: %12d%n",
+ infoTotalCputime, beanCputime, beanCputime - infoTotalCputime);
+ } else {
+ break; // nothing to compare; continue
+ }
+ }
+ }
+ /**
+ * Check two Durations, the second should be greater than the first or
+ * within the supplied Epsilon.
+ * @param d1 a Duration - presumed to be shorter
+ * @param d2 a 2nd Duration - presumed to be greater (or within Epsilon)
+ * @param epsilon Epsilon the amount of overlap allowed
+ * @return
+ */
+ static boolean checkEpsilon(Duration d1, Duration d2, Duration epsilon) {
+ if (d1.toNanos() <= d2.toNanos()) {
+ return true;
+ }
+ Duration diff = d1.minus(d2).abs();
+ return diff.compareTo(epsilon) <= 0;
+ }
+
+ /**
+ * Spawn a native process with the provided arguments.
+ * @param command the executable of native process
+ * @args
+ * @return the Process that was started
+ * @throws IOException thrown by ProcessBuilder.start
+ */
+ static Process spawn(String command, String... args) throws IOException {
+ ProcessBuilder pb = new ProcessBuilder();
+ pb.inheritIO();
+ List<String> list = new ArrayList<>();
+ list.add(command);
+ for (String arg : args)
+ list.add(arg);
+ pb.command(list);
+ return pb.start();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/JavaChild.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,524 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import com.sun.management.OperatingSystemMXBean;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.PrintWriter;
+import java.lang.InterruptedException;
+import java.lang.Override;
+import java.lang.management.ManagementFactory;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.CompletableFuture;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+
+/**
+ * Command driven subprocess with useful child functions.
+ */
+public class JavaChild extends Process {
+
+private static volatile int commandSeq = 0; // Command sequence number
+ private static final ProcessHandle self = ProcessHandle.current();
+ private static int finalStatus = 0;
+ private static final List<JavaChild> children = new ArrayList<>();
+ private static final Set<JavaChild> completedChildren =
+ Collections.synchronizedSet(new HashSet<>());
+
+ private final Process delegate;
+ private final PrintWriter inputWriter;
+ private final BufferedReader outputReader;
+
+
+ /**
+ * Create a JavaChild control instance that delegates to the spawned process.
+ * {@link #sendAction} is used to send commands via the processes stdin.
+ * {@link #forEachOutputLine} can be used to process output from the child
+ * @param delegate the process to delegate and send commands to and get responses from
+ */
+ private JavaChild(Process delegate) {
+ this.delegate = delegate;
+ // Initialize PrintWriter with autoflush (on println)
+ inputWriter = new PrintWriter(delegate.getOutputStream(), true);
+ outputReader = new BufferedReader(new InputStreamReader(delegate.getInputStream()));
+ }
+
+ @Override
+ public void destroy() {
+ delegate.destroy();
+ }
+
+ @Override
+ public int exitValue() {
+ return delegate.exitValue();
+ }
+
+ @Override
+ public int waitFor() throws InterruptedException {
+ return delegate.waitFor();
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ return delegate.getOutputStream();
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ return delegate.getInputStream();
+ }
+
+ @Override
+ public InputStream getErrorStream() {
+ return delegate.getErrorStream();
+ }
+
+ @Override
+ public ProcessHandle toHandle() {
+ return delegate.toHandle();
+ }
+
+ @Override
+ public CompletableFuture<Process> onExit() {
+ return delegate.onExit();
+ }
+ @Override
+ public String toString() {
+ return "delegate: " + delegate.toString();
+ }
+
+ public CompletableFuture<JavaChild> onJavaChildExit() {
+ return onExit().thenApply(ph -> this);
+ }
+
+ /**
+ * Send an action and arguments to the child via stdin.
+ * @param action the action
+ * @param args additional arguments
+ * @throws IOException if something goes wrong writing to the child
+ */
+ void sendAction(String action, Object... args) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ sb.append(action);
+ for (Object arg :args) {
+ sb.append(" ");
+ sb.append(arg);
+ }
+ String cmd = sb.toString();
+ synchronized (this) {
+ inputWriter.println(cmd);
+ }
+ }
+
+ public BufferedReader outputReader() {
+ return outputReader;
+ }
+
+ /**
+ * Asynchronously evaluate each line of output received back from the child process.
+ * @param consumer a Consumer of each line read from the child
+ * @return a CompletableFuture that is completed when the child closes System.out.
+ */
+ CompletableFuture<String> forEachOutputLine(Consumer<String> consumer) {
+ final CompletableFuture<String> future = new CompletableFuture<>();
+ String name = "OutputLineReader-" + getPid();
+ Thread t = new Thread(() -> {
+ try (BufferedReader reader = outputReader()) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ consumer.accept(line);
+ }
+ } catch (IOException | RuntimeException ex) {
+ consumer.accept("IOE (" + getPid() + "):" + ex.getMessage());
+ future.completeExceptionally(ex);
+ }
+ future.complete("success");
+ }, name);
+ t.start();
+ return future;
+ }
+
+ /**
+ * Spawn a JavaChild with the provided arguments.
+ * Commands can be send to the child with {@link #sendAction}.
+ * Output lines from the child can be processed with {@link #forEachOutputLine}.
+ * System.err is set to inherit and is the unstructured async logging
+ * output for all subprocesses.
+ * @param args the command line arguments to JavaChild
+ * @return the JavaChild that was started
+ * @throws IOException thrown by ProcessBuilder.start
+ */
+ static JavaChild spawnJavaChild(Object... args) throws IOException {
+ String[] stringArgs = new String[args.length];
+ for (int i = 0; i < args.length; i++) {
+ stringArgs[i] = args[i].toString();
+ }
+ ProcessBuilder pb = build(stringArgs);
+ pb.redirectError(ProcessBuilder.Redirect.INHERIT);
+ return new JavaChild(pb.start());
+ }
+
+ /**
+ * Spawn a JavaChild with the provided arguments.
+ * Sets the process to inherit the I/O channels.
+ * @param args the command line arguments to JavaChild
+ * @return the Process that was started
+ * @throws IOException thrown by ProcessBuilder.start
+ */
+ static Process spawn(String... args) throws IOException {
+ ProcessBuilder pb = build(args);
+ pb.inheritIO();
+ return pb.start();
+ }
+
+ /**
+ * Return a ProcessBuilder with the javaChildArgs and
+ * any additional supplied args.
+ *
+ * @param args the command line arguments to JavaChild
+ * @return the ProcessBuilder
+ */
+ static ProcessBuilder build(String ... args) {
+ ProcessBuilder pb = new ProcessBuilder();
+ List<String> list = new ArrayList<>(javaChildArgs);
+ for (String arg : args)
+ list.add(arg);
+ pb.command(list);
+ return pb;
+ }
+
+ static final String javaHome = (System.getProperty("test.jdk") != null)
+ ? System.getProperty("test.jdk")
+ : System.getProperty("java.home");
+
+ static final String javaExe =
+ javaHome + File.separator + "bin" + File.separator + "java";
+
+ static final String classpath =
+ System.getProperty("java.class.path");
+
+ static final List<String> javaChildArgs =
+ Arrays.asList(javaExe,
+ "-XX:+DisplayVMOutputToStderr",
+ "-Dtest.jdk=" + javaHome,
+ "-classpath", absolutifyPath(classpath),
+ "JavaChild");
+
+ private static String absolutifyPath(String path) {
+ StringBuilder sb = new StringBuilder();
+ for (String file : path.split(File.pathSeparator)) {
+ if (sb.length() != 0)
+ sb.append(File.pathSeparator);
+ sb.append(new File(file).getAbsolutePath());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Main program that interprets commands from the command line args or stdin.
+ * Each command produces output to stdout confirming the command and
+ * providing results.
+ * System.err is used for unstructured information.
+ * @param args an array of strings to be interpreted as commands;
+ * each command uses additional arguments as needed
+ */
+ public static void main(String[] args) {
+ System.out.printf("args: %s %s%n", ProcessHandle.current(), Arrays.toString(args));
+ interpretCommands(args);
+ System.exit(finalStatus);
+ }
+
+ /**
+ * Interpret an array of strings as a command line.
+ * @param args an array of strings to be interpreted as commands;
+ * each command uses additional arguments as needed
+ */
+ private static void interpretCommands(String[] args) {
+ try {
+ int nextArg = 0;
+ while (nextArg < args.length) {
+ String action = args[nextArg++];
+ switch (action) {
+ case "help":
+ sendResult(action, "");
+ help();
+ break;
+ case "sleep":
+ int millis = Integer.valueOf(args[nextArg++]);
+ Thread.sleep(millis);
+ sendResult(action, Integer.toString(millis));
+ break;
+ case "cpuloop":
+ long times = Long.valueOf(args[nextArg++]);
+ Instant end = Instant.now().plusMillis(times);
+ while (Instant.now().isBefore(end)) {
+ // burn the cpu til the time is up
+ }
+ sendResult(action, times);
+ break;
+ case "cputime":
+ sendResult(action, getCpuTime());
+ break;
+ case "out":
+ case "err":
+ String value = args[nextArg++];
+ sendResult(action, value);
+ if (action.equals("err")) {
+ System.err.println(value);
+ }
+ break;
+ case "stdin":
+ // Read commands from stdin; at eof, close stdin of
+ // children and wait for each to exit
+ sendResult(action, "start");
+ try (Reader reader = new InputStreamReader(System.in);
+ BufferedReader input = new BufferedReader(reader)) {
+ String line;
+ while ((line = input.readLine()) != null) {
+ line = line.trim();
+ if (!line.isEmpty()) {
+ String[] split = line.split("\\s");
+ interpretCommands(split);
+ }
+ }
+ // EOF on stdin, close stdin on all spawned processes
+ for (JavaChild p : children) {
+ try {
+ p.getOutputStream().close();
+ } catch (IOException ie) {
+ sendResult("stdin_closing", p.getPid(),
+ "exception", ie.getMessage());
+ }
+ }
+
+ for (JavaChild p : children) {
+ do {
+ try {
+ p.waitFor();
+ break;
+ } catch (InterruptedException e) {
+ // retry
+ }
+ } while (true);
+ }
+ // Wait for all children to be gone
+ Instant timeOut = Instant.now().plusSeconds(10L);
+ while (!completedChildren.containsAll(children)) {
+ if (Instant.now().isBefore(timeOut)) {
+ Thread.sleep(100L);
+ } else {
+ System.err.printf("Timeout waiting for " +
+ "children to terminate%n");
+ children.removeAll(completedChildren);
+ for (JavaChild c : children) {
+ sendResult("stdin_noterm", c.getPid());
+ System.err.printf(" Process not terminated: " +
+ "pid: %d%n", c.getPid());
+ }
+ System.exit(2);
+ }
+ }
+ }
+ sendResult(action, "done");
+ return; // normal exit from JavaChild Process
+ case "parent":
+ sendResult(action, self.parent().toString());
+ break;
+ case "pid":
+ sendResult(action, self.toString());
+ break;
+ case "exit":
+ int exitValue = (nextArg < args.length)
+ ? Integer.valueOf(args[nextArg]) : 0;
+ sendResult(action, exitValue);
+ System.exit(exitValue);
+ break;
+ case "spawn": {
+ if (args.length - nextArg < 2) {
+ throw new RuntimeException("not enough args for respawn: " +
+ (args.length - 2));
+ }
+ // Spawn as many children as requested and
+ // pass on rest of the arguments
+ int ncount = Integer.valueOf(args[nextArg++]);
+ Object[] subargs = new String[args.length - nextArg];
+ System.arraycopy(args, nextArg, subargs, 0, subargs.length);
+ for (int i = 0; i < ncount; i++) {
+ JavaChild p = spawnJavaChild(subargs);
+ sendResult(action, p.getPid());
+ p.forEachOutputLine(JavaChild::sendRaw);
+ p.onJavaChildExit().thenAccept((p1) -> {
+ int excode = p1.exitValue();
+ sendResult("child_exit", p1.getPid(), excode);
+ completedChildren.add(p1);
+ });
+ children.add(p); // Add child to spawned list
+ }
+ nextArg = args.length;
+ break;
+ }
+ case "child": {
+ // Send the command to all the live children;
+ // ignoring those that are not alive
+ int sentCount = 0;
+ Object[] result =
+ Arrays.copyOfRange(args, nextArg - 1, args.length);
+ Object[] subargs =
+ Arrays.copyOfRange(args, nextArg + 1, args.length);
+ for (JavaChild p : children) {
+ if (p.isAlive()) {
+ sentCount++;
+ // overwrite with current pid
+ result[0] = Long.toString(p.getPid());
+ sendResult(action, result);
+ p.sendAction(args[nextArg], subargs);
+ }
+ }
+ if (sentCount == 0) {
+ sendResult(action, "n/a");
+ }
+ nextArg = args.length;
+ break;
+ }
+ case "child_eof" :
+ // Close the InputStream of all the live children;
+ // ignoring those that are not alive
+ for (JavaChild p : children) {
+ if (p.isAlive()) {
+ sendResult(action, p.getPid());
+ p.getOutputStream().close();
+ }
+ }
+ break;
+ case "property":
+ String name = args[nextArg++];
+ sendResult(action, name, System.getProperty(name));
+ break;
+ case "threaddump":
+ Thread.dumpStack();
+ break;
+ default:
+ throw new Error("JavaChild action unknown: " + action);
+ }
+ }
+ } catch (Throwable t) {
+ t.printStackTrace(System.err);
+ System.exit(1);
+ }
+ }
+
+ static synchronized void sendRaw(String s) {
+ System.out.println(s);
+ System.out.flush();
+ }
+ static void sendResult(String action, Object... results) {
+ sendRaw(new Event(action, results).toString());
+ }
+
+ static long getCpuTime() {
+ OperatingSystemMXBean osMbean =
+ (OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean();
+ return osMbean.getProcessCpuTime();
+ }
+
+ /**
+ * Print command usage to stderr.
+ */
+ private static void help() {
+ System.err.println("Commands:");
+ System.err.println(" help");
+ System.err.println(" pid");
+ System.err.println(" parent");
+ System.err.println(" cpuloop <loopcount>");
+ System.err.println(" cputime");
+ System.err.println(" stdin - read commands from stdin");
+ System.err.println(" sleep <millis>");
+ System.err.println(" spawn <n> command... - spawn n new children and send command");
+ System.err.println(" child command... - send command to all live children");
+ System.err.println(" child_eof - send eof to all live children");
+ System.err.println(" exit <exitcode>");
+ System.err.println(" out arg...");
+ System.err.println(" err arg...");
+ }
+
+ static class Event {
+ long pid;
+ long seq;
+ String command;
+ Object[] results;
+ Event(String command, Object... results) {
+ this(self.getPid(), ++commandSeq, command, results);
+ }
+ Event(long pid, int seq, String command, Object... results) {
+ this.pid = pid;
+ this.seq = seq;
+ this.command = command;
+ this.results = results;
+ }
+
+ /**
+ * Create a String encoding the pid, seq, command, and results.
+ *
+ * @return a String formatted to send to the stream.
+ */
+ String format() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(pid);
+ sb.append(":");
+ sb.append(seq);
+ sb.append(" ");
+ sb.append(command);
+ for (int i = 0; i < results.length; i++) {
+ sb.append(" ");
+ sb.append(results[i]);
+ }
+ return sb.toString();
+ }
+
+ Event(String encoded) {
+ String[] split = encoded.split("\\s");
+ String[] pidSeq = split[0].split(":");
+ pid = Long.valueOf(pidSeq[0]);
+ seq = Integer.valueOf(pidSeq[1]);
+ command = split[1];
+ Arrays.copyOfRange(split, 1, split.length);
+ }
+
+ public String toString() {
+ return format();
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/OnExitTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.lang.InterruptedException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+import jdk.testlibrary.Platform;
+import org.testng.annotations.Test;
+import org.testng.Assert;
+import org.testng.TestNG;
+
+/*
+ * @test
+ * @library /lib/testlibrary
+ * @summary Functions of Process.onExit and ProcessHandle.onExit
+ * @author Roger Riggs
+ */
+
+public class OnExitTest extends ProcessUtil {
+
+ @SuppressWarnings("raw_types")
+ public static void main(String[] args) {
+ Class<?>[] testclass = { OnExitTest.class};
+ TestNG testng = new TestNG();
+ testng.setTestClasses(testclass);
+ testng.run();
+ }
+
+ /**
+ * Basic test of exitValue and onExit.
+ */
+ @Test
+ public static void test1() {
+ try {
+ int[] exitValues = {0, 1, 10};
+ for (int value : exitValues) {
+ Process p = JavaChild.spawn("exit", Integer.toString(value));
+ CompletableFuture<Process> future = p.onExit();
+ future.thenAccept( (ph) -> {
+ int actualExitValue = ph.exitValue();
+ printf(" javaChild done: %s, exitStatus: %d%n",
+ ph, actualExitValue);
+ Assert.assertEquals(actualExitValue, value, "actualExitValue incorrect");
+ Assert.assertEquals(ph, p, "Different Process passed to thenAccept");
+ });
+
+ Process h = future.get();
+ Assert.assertEquals(h, p);
+ Assert.assertEquals(p.exitValue(), value);
+ Assert.assertFalse(p.isAlive(), "Process should not be alive");
+ p.waitFor();
+ }
+ } catch (IOException | InterruptedException | ExecutionException ex) {
+ Assert.fail(ex.getMessage(), ex);
+ } finally {
+ destroyProcessTree(ProcessHandle.current());
+ }
+ }
+
+ /**
+ * Test of Completion handler when parent is killed.
+ * Spawn 1 child to spawn 3 children each with 2 children.
+ */
+ @Test
+ public static void test2() {
+ try {
+ ConcurrentHashMap<ProcessHandle, ProcessHandle> processes = new ConcurrentHashMap<>();
+ List<ProcessHandle> children = getChildren(ProcessHandle.current());
+ children.forEach(ProcessUtil::printProcess);
+ Assert.assertEquals(children.size(), 0,
+ "Expected to start with zero children; " + children);
+
+ JavaChild proc = JavaChild.spawnJavaChild("stdin");
+ ProcessHandle procHandle = proc.toHandle();
+ printf(" spawned: %d%n", proc.getPid());
+
+ proc.forEachOutputLine((s) -> {
+ String[] split = s.trim().split(" ");
+ if (split.length == 3 && split[1].equals("spawn")) {
+ Long child = Long.valueOf(split[2]);
+ Long parent = Long.valueOf(split[0].split(":")[0]);
+ processes.put(ProcessHandle.of(child).get(), ProcessHandle.of(parent).get());
+ }
+ });
+
+ proc.sendAction("spawn", "3", "stdin");
+
+ proc.sendAction("child", "spawn", "2", "stdin");
+
+ // Poll until all 9 child processes exist or the timeout is reached
+ int expected = 9;
+ Instant endTimeout = Instant.now().plusSeconds(10L);
+ do {
+ Thread.sleep(200L);
+ printf(" subprocess count: %d, waiting for %d%n", processes.size(), expected);
+ } while (processes.size() < expected &&
+ Instant.now().isBefore(endTimeout));
+
+ children = getAllChildren(procHandle);
+
+ ArrayBlockingQueue<ProcessHandle> completions = new ArrayBlockingQueue<>(expected + 1);
+ Instant startTime = Instant.now();
+ // Create a future for each of the 9 children
+ processes.forEach( (p, parent) -> {
+ p.onExit().whenComplete((ph, ex) -> {
+ Duration elapsed = Duration.between(startTime, Instant.now());
+ completions.add(ph);
+ printf("whenComplete: pid: %s, exception: %s, thread: %s, elapsed: %s%n",
+ ph, ex, Thread.currentThread(), elapsed);
+ });
+ });
+
+ // Check that each of the spawned processes is included in the children
+ List<ProcessHandle> remaining = new ArrayList<>(children);
+ processes.forEach((p, parent) -> {
+ Assert.assertTrue(remaining.remove(p), "spawned process should have been in children");
+ });
+
+ // Remove Win32 system spawned conhost.exe processes
+ remaining.removeIf(ProcessUtil::isWindowsConsole);
+
+ remaining.forEach(p -> printProcess(p, "unexpected: "));
+ if (remaining.size() > 0) {
+ // Show full list for debugging
+ ProcessUtil.logTaskList();
+ }
+
+ proc.destroy(); // kill off the parent
+ proc.waitFor();
+
+ // Wait for all the processes to be completed
+ processes.forEach((p, parent) -> {
+ try {
+ p.onExit().get();
+ } catch (InterruptedException | ExecutionException ex) {
+ // ignore
+ }
+ });
+
+ // Verify that all 9 exit handlers were called
+ processes.forEach((p, parent) ->
+ Assert.assertTrue(completions.contains(p), "Child onExit not called: " + p
+ + ", parent: " + parent
+ + ": " + p.info()));
+
+ // Show the status of the original children
+ children.forEach(p -> printProcess(p, "after onExit:"));
+
+ Assert.assertEquals(proc.isAlive(), false, "destroyed process is alive:: %s%n" + proc);
+
+ List<ProcessHandle> children2 = getAllChildren(procHandle);
+ printf(" children2: %s%n", children2.toString());
+ Assert.assertEquals(children2.size(), 0, "After onExit, expected no children");
+
+ Assert.assertEquals(remaining.size(), 0, "Unaccounted for children");
+
+ } catch (IOException | InterruptedException ex) {
+ Assert.fail(ex.getMessage());
+ } finally {
+ destroyProcessTree(ProcessHandle.current());
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/PermissionTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.FilePermission;
+import java.io.IOException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.security.SecurityPermission;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.PropertyPermission;
+
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.Test;
+
+public class PermissionTest {
+ /**
+ * Backing up policy.
+ */
+ protected static Policy policy;
+
+ /**
+ * Backing up security manager.
+ */
+ private static SecurityManager sm;
+
+ /**
+ * Current process handle.
+ */
+ private final ProcessHandle currentHndl;
+
+ PermissionTest() {
+ policy = Policy.getPolicy();
+ sm = System.getSecurityManager();
+ currentHndl = ProcessHandle.current();
+ }
+
+ @Test
+ public void allChildrenWithPermission() {
+ Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+ currentHndl.allChildren();
+ }
+
+ @Test
+ public void allProcessesWithPermission() {
+ Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+ ProcessHandle.allProcesses();
+ }
+
+ @Test
+ public void childrenWithPermission() {
+ Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+ currentHndl.children();
+ }
+
+ @Test
+ public void currentWithPermission() {
+ Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+ ProcessHandle.current();
+ }
+
+ @Test
+ public void ofWithPermission() {
+ Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+ ProcessHandle.of(0);
+ }
+
+ @Test
+ public void parentWithPermission() {
+ Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+ currentHndl.parent();
+ }
+
+ @Test
+ public void processToHandleWithPermission() throws IOException {
+ Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+ Process p = null;
+ try {
+ ProcessBuilder pb = new ProcessBuilder("sleep", "30");
+ p = pb.start();
+ ProcessHandle ph = p.toHandle();
+ Assert.assertNotNull(ph, "ProcessHandle expected from Process");
+ } finally {
+ if (p != null) {
+ p.destroy();
+ }
+ }
+ }
+
+ @BeforeGroups (groups = {"NoManageProcessPermission"})
+ public void noPermissionsSetup(){
+ Policy.setPolicy(new TestPolicy());
+ SecurityManager sm = new SecurityManager();
+ System.setSecurityManager(sm);
+ }
+
+ @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+ public void noPermissionAllChildren() {
+ currentHndl.allChildren();
+ }
+
+ @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+ public void noPermissionAllProcesses() {
+ ProcessHandle.allProcesses();
+ }
+
+ @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+ public void noPermissionChildren() {
+ currentHndl.children();
+ }
+
+ @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+ public void noPermissionCurrent() {
+ ProcessHandle.current();
+ }
+
+ @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+ public void noPermissionOf() {
+ ProcessHandle.of(0);
+ }
+
+ @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+ public void noPermissionParent() {
+ currentHndl.parent();
+ }
+
+ @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+ public void noPermissionProcessToHandle() throws IOException {
+ Process p = null;
+ try {
+ ProcessBuilder pb = new ProcessBuilder("sleep", "30");
+ p = pb.start();
+ ProcessHandle ph = p.toHandle();
+ Assert.assertNotNull(ph, "ProcessHandle expected from Process");
+ } finally {
+ if (p != null) {
+ p.destroy();
+ }
+ }
+ }
+
+ @AfterClass
+ public void tearDownClass() throws Exception {
+ System.setSecurityManager(sm);
+ Policy.setPolicy(policy);
+ }
+}
+
+class TestPolicy extends Policy {
+ private final PermissionCollection permissions = new Permissions();
+
+ public TestPolicy() {
+ setBasicPermissions();
+ }
+
+ /*
+ * Defines the minimal permissions required by testNG and set security
+ * manager permission when running these tests.
+ */
+ public void setBasicPermissions() {
+ permissions.add(new SecurityPermission("getPolicy"));
+ permissions.add(new SecurityPermission("setPolicy"));
+ permissions.add(new RuntimePermission("getClassLoader"));
+ permissions.add(new RuntimePermission("setSecurityManager"));
+ permissions.add(new RuntimePermission("createSecurityManager"));
+ permissions.add(new PropertyPermission("testng.show.stack.frames",
+ "read"));
+ permissions.add(new PropertyPermission("user.dir", "read"));
+ permissions.add(new PropertyPermission("test.src", "read"));
+ permissions.add(new PropertyPermission("file.separator", "read"));
+ permissions.add(new PropertyPermission("line.separator", "read"));
+ permissions.add(new PropertyPermission("fileStringBuffer", "read"));
+ permissions.add(new PropertyPermission("dataproviderthreadcount", "read"));
+ permissions.add(new FilePermission("<<ALL FILES>>", "execute"));
+ }
+
+ public TestPolicy(Permission... ps) {
+ setBasicPermissions();
+ Arrays.stream(ps).forEach(p -> permissions.add(p));
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return permissions;
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return permissions;
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission perm) {
+ return permissions.implies(perm);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/ProcessUtil.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.ProcessBuilder;
+import java.time.Duration;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import com.sun.management.OperatingSystemMXBean;
+
+import jdk.testlibrary.Platform;
+
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Useful utilities for testing Process and ProcessHandle.
+ */
+public abstract class ProcessUtil {
+ /**
+ * Constructor
+ */
+ public ProcessUtil() {}
+
+ /**
+ * Returns the list of direct children.
+ * WIndows conhost.exe children are filtered out.
+ * @param ph the Process to get children of
+ * @return a list of child ProcessHandles
+ */
+ public static List<ProcessHandle> getChildren(ProcessHandle ph) {
+ return ph.children()
+ .filter(ProcessUtil::isNotWindowsConsole)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Returns the list of all direct and indirect children.
+ * WIndows conhost.exe children are filtered out.
+ * @param ph the Process to get children of
+ * @return a list of child ProcessHandles
+ */
+ public static List<ProcessHandle> getAllChildren(ProcessHandle ph) {
+ return ph.allChildren()
+ .filter(ProcessUtil::isNotWindowsConsole)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Waits for and returns the direct expected Children of a ProcessHandle.
+ * For Windows, the conhost.exe children are filtered out.
+ *
+ * @param ph the process to get the children of
+ * @param nchildren the minimum number of children to expect
+ * @return a list of ProcessHandles of the children.
+ */
+ public static List<ProcessHandle> waitForChildren(ProcessHandle ph, long nchildren) {
+ List<ProcessHandle> subprocesses = null;
+ long count = 0;
+ do {
+ if (subprocesses != null) {
+ // Only wait if this is not the first time looking
+ try {
+ Thread.sleep(500L); // It will happen but don't burn the cpu
+ } catch (InterruptedException ie) {
+ // ignore
+ }
+ }
+ subprocesses = getChildren(ph);
+ count = subprocesses.size();
+ System.out.printf(" waiting for subprocesses of %s to start," +
+ " expected: %d, current: %d%n", ph, nchildren, count);
+ } while (count < nchildren);
+ return subprocesses;
+ }
+
+ /**
+ * Waits for and returns all expected Children of a ProcessHandle.
+ * For Windows, the conhost.exe children are filtered out.
+ *
+ * @param ph the process to get the children of
+ * @param nchildren the minimum number of children to expect
+ * @return a list of ProcessHandles of the children.
+ */
+ public static List<ProcessHandle> waitForAllChildren(ProcessHandle ph, long nchildren) {
+ List<ProcessHandle> subprocesses = null;
+ long count = 0;
+ do {
+ if (subprocesses != null) {
+ // Only wait if this is not the first time looking
+ try {
+ Thread.sleep(500L); // It will happen but don't burn the cpu
+ } catch (InterruptedException ie) {
+ // ignore
+ }
+ }
+ subprocesses = getAllChildren(ph);
+ count = subprocesses.size();
+ System.out.printf(" waiting for subprocesses of %s to start," +
+ " expected: %d, current: %d%n", ph, nchildren, count);
+ } while (count < nchildren);
+ return subprocesses;
+ }
+
+ /**
+ * Destroy all children of the ProcessHandle.
+ * (Except the conhost.exe on Windows)
+ *
+ * @param p a ProcessHandle
+ * @return the ProcessHandle
+ */
+ public static ProcessHandle destroyProcessTree(ProcessHandle p) {
+ Stream<ProcessHandle> children = p.allChildren().filter(ProcessUtil::isNotWindowsConsole);
+ children.forEach(ph -> {
+ System.out.printf("destroyProcessTree destroyForcibly%n");
+ printProcess(ph);
+ ph.destroyForcibly();
+ });
+ return p;
+ }
+
+ /**
+ * The OSMXBean for this process.
+ */
+ public static final OperatingSystemMXBean osMbean =
+ (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
+
+ /**
+ * Return the CPU time of the current process according to the OperatingSystemMXBean.
+ *
+ * @return the CPU time of the current process
+ */
+ public static Duration MXBeanCpuTime() {
+ return Duration.ofNanos(osMbean.getProcessCpuTime());
+ }
+
+ /**
+ * Return true if the ProcessHandle is a Windows i586 conhost.exe process.
+ *
+ * @param p the processHandle of the Process
+ * @return Return true if the ProcessHandle is for a Windows i586 conhost.exe process
+ */
+ static boolean isWindowsConsole(ProcessHandle p) {
+ return Platform.isWindows() && p.info().command().orElse("").endsWith("C:\\Windows\\System32\\conhost.exe");
+ }
+
+ /**
+ * Return true if the ProcessHandle is NOT a Windows i586 conhost.exe process.
+ *
+ * @param p the processHandle of the Process
+ * @return Return true if the ProcessHandle is NOT for a Windows i586 conhost.exe process
+ */
+ static boolean isNotWindowsConsole(ProcessHandle p) {
+ return !isWindowsConsole(p);
+ }
+
+ /**
+ * Print a formatted string to System.out.
+ * @param format the format
+ * @param args the argument array
+ */
+ static void printf(String format, Object... args) {
+ String s = String.format(format, args);
+ System.out.print(s);
+ }
+
+ /**
+ * Print information about a process.
+ * Prints the pid, if it is alive, and information about the process.
+ * @param ph the processHandle at the top
+ */
+ static void printProcess(ProcessHandle ph) {
+ printProcess(ph, "");
+ }
+
+ /**
+ * Print information about a process.
+ * Prints the pid, if it is alive, and information about the process.
+ * @param ph the processHandle at the top
+ * @param prefix the String to prefix the output with
+ */
+ static void printProcess(ProcessHandle ph, String prefix) {
+ printf("%spid %s, alive: %s; parent: %s, %s%n", prefix,
+ ph.getPid(), ph.isAlive(), ph.parent(), ph.info());
+ }
+
+ /**
+ * Print the process hierarchy as visible via ProcessHandle.
+ * Prints the pid, if it is alive, and information about the process.
+ * @param ph the processHandle at the top
+ * @param prefix the String to prefix the output with
+ */
+ static void printDeep(ProcessHandle ph, String prefix) {
+ printProcess(ph, prefix);
+ ph.children().forEach(p -> printDeep(p, prefix + " "));
+ }
+
+ /**
+ * Use the native command to list the active processes.
+ */
+ static void logTaskList() {
+ String[] windowsArglist = {"tasklist.exe", "/v"};
+ String[] unixArglist = {"ps", "-ef"};
+
+ String[] argList = null;
+ if (Platform.isWindows()) {
+ argList = windowsArglist;
+ } else if (Platform.isLinux() || Platform.isOSX()) {
+ argList = unixArglist;
+ } else {
+ return;
+ }
+
+ ProcessBuilder pb = new ProcessBuilder(argList);
+ pb.inheritIO();
+ try {
+ Process proc = pb.start();
+ proc.waitFor();
+ } catch (IOException | InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/TEST.properties Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,4 @@
+# ProcessHandle tests use TestNG
+TestNG.dirs = .
+lib.dirs = /lib/testlibrary
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/TreeTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.concurrent.ExecutionException;
+import org.testng.Assert;
+import org.testng.TestNG;
+import org.testng.annotations.Test;
+
+/*
+ * @test
+ * @library /lib/testlibrary
+ * Test counting and JavaChild.spawning and counting of Processes.
+ * @run testng/othervm InfoTest
+ * @author Roger Riggs
+ */
+public class TreeTest extends ProcessUtil {
+ // Main can be used to run the tests from the command line with only testng.jar.
+ @SuppressWarnings("raw_types")
+ public static void main(String[] args) {
+ Class<?>[] testclass = {TreeTest.class};
+ TestNG testng = new TestNG();
+ testng.setTestClasses(testclass);
+ testng.run();
+ }
+
+ /**
+ * Test counting and spawning and counting of Processes.
+ */
+ @Test
+ public static void test1() {
+ final int MAXCHILDREN = 2;
+ List<JavaChild> spawned = new ArrayList<>();
+
+ try {
+ ProcessHandle self = ProcessHandle.current();
+
+ printf("self pid: %d%n", self.getPid());
+ printDeep(self, "");
+ long count = getChildren(self).size();
+ Assert.assertEquals(count, 0, "Start with zero children");
+
+ for (int i = 0; i < MAXCHILDREN; i++) {
+ // spawn and wait for instructions
+ spawned.add(JavaChild.spawnJavaChild("pid", "stdin"));
+ }
+
+ List<ProcessHandle> subprocesses = getChildren(self);
+ subprocesses.forEach(ProcessUtil::printProcess);
+ count = subprocesses.size();
+ Assert.assertEquals(count, MAXCHILDREN, "Wrong number of spawned children");
+
+ // Send exit command to each spawned Process
+ spawned.forEach(p -> {
+ try {
+ p.sendAction("exit", "");
+ } catch (IOException ex) {
+ Assert.fail("IOException in sendAction", ex);
+ }
+ });
+
+ // Wait for each Process to exit
+ spawned.forEach(p -> {
+ do {
+ try {
+ Assert.assertEquals(p.waitFor(), 0, "exit status incorrect");
+ break;
+ } catch (InterruptedException ex) {
+ continue; // Retry
+ }
+ } while (true);
+ });
+
+ // Verify that ProcessHandle.isAlive sees each of them as not alive
+ for (ProcessHandle ph : subprocesses) {
+ Assert.assertFalse(ph.isAlive(),
+ "ProcessHandle.isAlive for exited process: " + ph);
+ }
+
+ // Verify no current children are visible
+ count = getChildren(self).size();
+ Assert.assertEquals(count, 0, "Children destroyed, should be zero");
+
+ } catch (IOException ioe) {
+ Assert.fail("unable to spawn process", ioe);
+ } finally {
+ // Cleanup any left over processes
+ spawned.stream().map(Process::toHandle)
+ .filter(ProcessHandle::isAlive)
+ .forEach(ph -> printDeep(ph, "test1 cleanup: "));
+ destroyProcessTree(ProcessHandle.current());
+ }
+ }
+
+ /**
+ * Test counting and spawning and counting of Processes.
+ */
+ @Test
+ public static void test2() {
+ ProcessHandle p1Handle = null;
+ try {
+ ProcessHandle self = ProcessHandle.current();
+ List<ProcessHandle> initialChildren = getChildren(self);
+ long count = initialChildren.size();
+ if (count > 0) {
+ initialChildren.forEach(p -> printDeep(p, "test2 initial unexpected: "));
+ Assert.assertEquals(count, 0, "Start with zero children (except Windows conhost.exe)");
+ }
+
+ JavaChild p1 = JavaChild.spawnJavaChild("stdin");
+ p1Handle = p1.toHandle();
+ printf(" p1 pid: %d%n", p1.getPid());
+
+ int spawnNew = 3;
+ p1.sendAction("spawn", spawnNew, "stdin");
+
+ // Wait for direct children to be created and save the list
+ List<ProcessHandle> subprocesses = waitForAllChildren(p1Handle, spawnNew);
+ for (ProcessHandle ph : subprocesses) {
+ Assert.assertTrue(ph.isAlive(), "Child should be alive: " + ph);
+ }
+
+ // Each child spawns two processes and waits for commands
+ int spawnNewSub = 2;
+ p1.sendAction("child", "spawn", spawnNewSub, "stdin");
+
+ // For each spawned child, wait for its children
+ for (ProcessHandle p : subprocesses) {
+ List<ProcessHandle> grandChildren = waitForChildren(p, spawnNewSub);
+ }
+
+ List<ProcessHandle> allChildren = getAllChildren(p1Handle);
+ printf(" allChildren: %s%n",
+ allChildren.stream().map(p -> p.getPid())
+ .collect(Collectors.toList()));
+ for (ProcessHandle ph : allChildren) {
+ Assert.assertEquals(ph.isAlive(), true, "Child should be alive: " + ph);
+ }
+
+ // Closing JavaChild's InputStream will cause all children to exit
+ p1.getOutputStream().close();
+
+ for (ProcessHandle p : allChildren) {
+ try {
+ p.onExit().get(); // wait for the child to exit
+ } catch (ExecutionException e) {
+ Assert.fail("waiting for process to exit", e);
+ }
+ }
+ p1.waitFor(); // wait for spawned process to exit
+
+ List<ProcessHandle> remaining = getChildren(self);
+ remaining.forEach(ph -> Assert.assertFalse(ph.isAlive(),
+ "process should not be alive: " + ph));
+ } catch (IOException | InterruptedException t) {
+ t.printStackTrace();
+ throw new RuntimeException(t);
+ } finally {
+ // Cleanup any left over processes
+ if (p1Handle.isAlive()) {
+ printDeep(p1Handle, "test2 cleanup: ");
+ }
+ destroyProcessTree(ProcessHandle.current());
+ }
+ }
+
+ /**
+ * Test destroy of processes.
+ */
+ @Test
+ public static void test3() {
+ try {
+ ProcessHandle self = ProcessHandle.current();
+
+ JavaChild p1 = JavaChild.spawnJavaChild("stdin");
+ ProcessHandle p1Handle = p1.toHandle();
+ printf(" p1: %s%n", p1.getPid());
+ long count = getChildren(self).size();
+ Assert.assertEquals(count, 1, "Wrong number of spawned children");
+
+ int newChildren = 3;
+ // Spawn children and have them wait
+ p1.sendAction("spawn", newChildren, "stdin");
+
+ // Wait for the new processes and save the list
+ List<ProcessHandle> subprocesses = waitForAllChildren(p1Handle, newChildren);
+ printDeep(p1Handle, "allChildren");
+
+ Assert.assertEquals(subprocesses.size(), newChildren, "Wrong number of children");
+
+ p1.children().filter(TreeTest::isNotWindowsConsole)
+ .forEach(ProcessHandle::destroyForcibly);
+
+ self.children().filter(TreeTest::isNotWindowsConsole)
+ .forEach(ProcessHandle::destroyForcibly);
+
+ do {
+ Thread.sleep(500L); // It will happen but don't burn the cpu
+ Object[] children = self.allChildren()
+ .filter(TreeTest::isNotWindowsConsole)
+ .toArray();
+ count = children.length;
+ printf(" waiting for subprocesses of %s to terminate," +
+ " expected: 0, current: %d, children: %s%n", self, count,
+ Arrays.toString(children));
+ printDeep(self, "");
+ } while (count > 0);
+
+ boolean ex1 = p1.waitFor(5, TimeUnit.SECONDS);
+ Assert.assertTrue(ex1, "Subprocess should have exited: " + p1);
+
+ for (ProcessHandle p : subprocesses) {
+ Assert.assertFalse(p.isAlive(), "Destroyed process.isAlive: " + p +
+ ", parent: " + p.parent() +
+ ", info: " + p.info().toString());
+ }
+
+ } catch (IOException ioe) {
+ Assert.fail("Spawn of subprocess failed", ioe);
+ } catch (InterruptedException inte) {
+ Assert.fail("InterruptedException", inte);
+ }
+ }
+
+ /**
+ * Test (Not really a test) that dumps the list of all Processes.
+ */
+ @Test
+ public static void test4() {
+ printf(" Parent Child Info%n");
+ Stream<ProcessHandle> s = ProcessHandle.allProcesses();
+ ProcessHandle[] processes = s.toArray(ProcessHandle[]::new);
+ int len = processes.length;
+ ProcessHandle[] parent = new ProcessHandle[len];
+ Set<ProcessHandle> processesSet =
+ Arrays.stream(processes).collect(Collectors.toSet());
+ Integer[] sortindex = new Integer[len];
+ for (int i = 0; i < len; i++) {
+ sortindex[i] = i;
+ }
+ for (int i = 0; i < len; i++) {
+ parent[sortindex[i]] = processes[sortindex[i]].parent().orElse(null);
+ }
+ Arrays.sort(sortindex, (i1, i2) -> {
+ int cmp = Long.compare((parent[i1] == null ? 0L : parent[i1].getPid()),
+ (parent[i2] == null ? 0L : parent[i2].getPid()));
+ if (cmp == 0) {
+ cmp = Long.compare((processes[i1] == null ? 0L : processes[i1].getPid()),
+ (processes[i2] == null ? 0L : processes[i2].getPid()));
+ }
+ return cmp;
+ });
+ boolean fail = false;
+ for (int i = 0; i < len; i++) {
+ ProcessHandle p = processes[sortindex[i]];
+ ProcessHandle p_parent = parent[sortindex[i]];
+ ProcessHandle.Info info = p.info();
+ String indent = " ";
+ if (p_parent != null) {
+ if (!processesSet.contains(p_parent)) {
+ fail = true;
+ indent = "*** ";
+ }
+ }
+ printf("%s %7s, %7s, %s%n", indent, p_parent, p, info);
+ }
+ Assert.assertFalse(fail, "Parents missing from all Processes");
+
+ }
+
+ /**
+ * A test for scale; launch a large number (39) of subprocesses.
+ */
+ @Test
+ public static void test5() {
+ int factor = 2;
+ ProcessHandle p1Handle = null;
+ Instant start = Instant.now();
+ try {
+ JavaChild p1 = JavaChild.spawnJavaChild("stdin");
+ p1Handle = p1.toHandle();
+
+ printf("Spawning %d x %d x %d processes, pid: %d%n",
+ factor, factor, factor, p1.getPid());
+
+ // Start the first tier of subprocesses
+ p1.sendAction("spawn", factor, "stdin");
+
+ // Start the second tier of subprocesses
+ p1.sendAction("child", "spawn", factor, "stdin");
+
+ // Start the third tier of subprocesses
+ p1.sendAction("child", "child", "spawn", factor, "stdin");
+
+ int newChildren = factor * (1 + factor * (1 + factor));
+ List<ProcessHandle> children = ProcessUtil.waitForAllChildren(p1Handle, newChildren);
+
+ Assert.assertEquals(p1.children()
+ .filter(ProcessUtil::isNotWindowsConsole)
+ .count(), factor, "expected direct children");
+ Assert.assertEquals(p1.allChildren()
+ .filter(ProcessUtil::isNotWindowsConsole)
+ .count(),
+ factor * factor * factor + factor * factor + factor,
+ "expected all children");
+
+ List<ProcessHandle> subprocesses = p1.allChildren()
+ .filter(ProcessUtil::isNotWindowsConsole)
+ .collect(Collectors.toList());
+ printf(" allChildren: %s%n",
+ subprocesses.stream().map(p -> p.getPid())
+ .collect(Collectors.toList()));
+
+ p1.getOutputStream().close(); // Close stdin for the controlling p1
+ p1.waitFor();
+ } catch (InterruptedException | IOException ex) {
+ Assert.fail("Unexpected Exception", ex);
+ } finally {
+ printf("Duration: %s%n", Duration.between(start, Instant.now()));
+ // Cleanup any left over processes
+ if (p1Handle.isAlive()) {
+ printDeep(p1Handle, "test5 cleanup: ");
+ }
+ destroyProcessTree(ProcessHandle.current());
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/String/LiteralReplace.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8058779
+ * @library /lib/testlibrary/
+ * @build jdk.testlibrary.RandomFactory
+ * @run testng LiteralReplace
+ * @summary Basic tests of String.replace(CharSequence, CharSequence)
+ * @key randomness
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.Random;
+
+import jdk.testlibrary.RandomFactory;
+
+import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
+import static org.testng.Assert.fail;
+
+public class LiteralReplace {
+
+ @Test(dataProvider="sourceTargetReplacementExpected")
+ public void testExpected(String source, String target,
+ String replacement, String expected)
+ {
+ String canonical = canonicalReplace(source, target, replacement);
+ if (!canonical.equals(expected)) {
+ fail("Canonical: " + canonical + " != " + expected);
+ }
+ test0(source, target, replacement, expected);
+ }
+
+ @Test(dataProvider="sourceTargetReplacement")
+ public void testCanonical(String source, String target,
+ String replacement)
+ {
+ String canonical = canonicalReplace(source, target, replacement);
+ test0(source, target, replacement, canonical);
+ }
+
+ private void test0(String source, String target, String replacement,
+ String expected)
+ {
+ String result = source.replace(target, replacement);
+ if (!result.equals(expected)) {
+ fail(result + " != " + expected);
+ }
+ }
+
+ @Test(dataProvider="sourceTargetReplacementWithNull",
+ expectedExceptions = {NullPointerException.class})
+ public void testNPE(String source, String target, String replacement) {
+ source.replace(target, replacement);
+ }
+
+
+ @DataProvider
+ public static Object[][] sourceTargetReplacementExpected() {
+ return new Object[][] {
+ {"aaa", "aa", "b", "ba"},
+ {"abcdefgh", "def", "DEF", "abcDEFgh"},
+ {"abcdefgh", "123", "DEF", "abcdefgh"},
+ {"abcdefgh", "abcdefghi", "DEF", "abcdefgh"},
+ {"abcdefghabc", "abc", "DEF", "DEFdefghDEF"},
+ {"abcdefghdef", "def", "", "abcgh"},
+ {"abcdefgh", "", "_", "_a_b_c_d_e_f_g_h_"},
+ {"", "", "", ""},
+ {"", "a", "b", ""},
+ {"", "", "abc", "abc"},
+ {"abcdefgh", "abcdefgh", "abcdefgh", "abcdefgh"},
+ {"abcdefgh", "abcdefgh", "abcdefghi", "abcdefghi"},
+ {"abcdefgh", "abcdefgh", "", ""},
+ {"abcdabcd", "abcd", "", ""},
+ {"aaaaaaaaa", "aa", "_X_", "_X__X__X__X_a"},
+ {"aaaaaaaaa", "aa", "aaa", "aaaaaaaaaaaaa"},
+ {"aaaaaaaaa", "aa", "aa", "aaaaaaaaa"},
+ {"a.c.e.g.", ".", "-", "a-c-e-g-"},
+ {"abcdefgh", "[a-h]", "X", "abcdefgh"},
+ {"aa+", "a+", "", "a"},
+ {"^abc$", "abc", "x", "^x$"},
+ };
+ }
+
+ @DataProvider
+ public static Iterator<Object[]> sourceTargetReplacement() {
+ ArrayList<Object[]> list = new ArrayList<>();
+ for (int maxSrcLen = 1; maxSrcLen <= (1 << 10); maxSrcLen <<= 1) {
+ for (int maxTrgLen = 1; maxTrgLen <= (1 << 10); maxTrgLen <<= 1) {
+ for (int maxPrlLen = 1; maxPrlLen <= (1 << 10); maxPrlLen <<= 1) {
+ list.add(makeArray(makeRandomString(maxSrcLen),
+ makeRandomString(maxTrgLen),
+ makeRandomString(maxPrlLen)));
+
+ String source = makeRandomString(maxSrcLen);
+ list.add(makeArray(source,
+ mekeRandomSubstring(source, maxTrgLen),
+ makeRandomString(maxPrlLen)));
+ }
+ }
+ }
+ return list.iterator();
+ }
+
+ @DataProvider
+ public static Iterator<Object[]> sourceTargetReplacementWithNull() {
+ ArrayList<Object[]> list = new ArrayList<>();
+ Object[] arr = {null, "", "a", "b", "string", "str", "ababstrstr"};
+ for (int i = 0; i < arr.length; ++i) {
+ for (int j = 0; j < arr.length; ++j) {
+ for (int k = 0; k < arr.length; ++k) {
+ if (arr[i] != null && (arr[j] == null || arr[k] == null)) {
+ list.add(makeArray(arr[i], arr[j], arr[k]));
+ }
+ }
+ }
+ }
+ return list.iterator();
+ }
+
+ // utilities
+
+ /**
+ * How the String.replace(CharSequence, CharSequence) used to be implemented
+ */
+ private static String canonicalReplace(String source, String target, String replacement) {
+ return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
+ source).replaceAll(Matcher.quoteReplacement(replacement.toString()));
+ }
+
+ private static final Random random = RandomFactory.getRandom();
+
+ private static final char[] CHARS = ("qwertyuiop[]12345678" +
+ "90-=\\`asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+|QWERTYUIOP{" +
+ "}ASDFGHJKL:\"ZXCVBNM<>?\n\r\t\u0444\u044B\u0432\u0430").toCharArray();
+
+ private static String makeRandomString(int maxLen) {
+ int len = random.nextInt(maxLen);
+ char[] buf = new char[len];
+ for (int i = 0; i < len; ++i) {
+ buf[i] = CHARS[random.nextInt(CHARS.length)];
+ }
+ return new String(buf);
+ }
+
+ private static String mekeRandomSubstring(String source, int maxLen) {
+ if (source.isEmpty()) {
+ return source;
+ }
+ int pos = random.nextInt(source.length());
+ int len = Integer.min(source.length() - pos,
+ random.nextInt(maxLen));
+ return source.substring(pos, pos + len);
+ }
+
+ private static Object[] makeArray(Object... array) {
+ return array;
+ }
+}
--- a/jdk/test/java/net/SocketOption/OptionsTest.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/java/net/SocketOption/OptionsTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -23,8 +23,9 @@
/*
* @test
- * @bug 8036979
+ * @bug 8036979 8072384
* @run main/othervm -Xcheck:jni OptionsTest
+ * @run main/othervm -Xcheck:jni -Djava.net.preferIPv4Stack=true OptionsTest
*/
import java.net.*;
@@ -59,7 +60,8 @@
static Test[] serverSocketTests = new Test[] {
Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
- Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE)
+ Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
+ Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
};
static Test[] dgSocketTests = new Test[] {
@@ -193,6 +195,9 @@
return Integer.valueOf(socket.getReceiveBufferSize());
} else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
return Boolean.valueOf(socket.getReuseAddress());
+ } else if (option.equals(StandardSocketOptions.IP_TOS)) {
+ return Integer.valueOf(jdk.net.Sockets.getOption(
+ socket, StandardSocketOptions.IP_TOS));
} else {
throw new RuntimeException("unexecpted socket option");
}
--- a/jdk/test/java/rmi/activation/rmidViaInheritedChannel/RmidViaInheritedChannel.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/java/rmi/activation/rmidViaInheritedChannel/RmidViaInheritedChannel.java Thu Jun 04 18:49:37 2015 -0700
@@ -34,6 +34,7 @@
* java.rmi/sun.rmi.transport.tcp
* @build TestLibrary RMID ActivationLibrary
* @run main/othervm/timeout=240 RmidViaInheritedChannel
+ * @key intermittent
*/
import java.io.IOException;
--- a/jdk/test/java/time/tck/java/time/zone/TCKZoneOffsetTransitionRule.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/java/time/tck/java/time/zone/TCKZoneOffsetTransitionRule.java Thu Jun 04 18:49:37 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -156,6 +156,13 @@
OFFSET_0200, OFFSET_0200, OFFSET_0300);
}
+ @Test(expectedExceptions=IllegalArgumentException.class)
+ public void test_factory_nonZeroTimeNanos() {
+ ZoneOffsetTransitionRule.of(
+ Month.MARCH, 20, DayOfWeek.SUNDAY, LocalTime.of(1, 2, 3, 400_000_000),
+ false, TimeDefinition.WALL, OFFSET_0200, OFFSET_0200, OFFSET_0300);
+ }
+
//-----------------------------------------------------------------------
// getters
//-----------------------------------------------------------------------
--- a/jdk/test/java/util/Arrays/ParallelPrefix.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/java/util/Arrays/ParallelPrefix.java Thu Jun 04 18:49:37 2015 -0700
@@ -51,7 +51,7 @@
private final static int MEDIUM_ARRAY_SIZE = 1 << 8;
//Array size much greater than MIN_PARTITION
- private final static int LARGE_ARRAY_SIZE = 1 << 12;
+ private final static int LARGE_ARRAY_SIZE = 1 << 14;
private final static int[] ARRAY_SIZE_COLLECTION = new int[]{
SMALL_ARRAY_SIZE,
@@ -60,7 +60,7 @@
LARGE_ARRAY_SIZE
};
- @DataProvider
+ @DataProvider(name = "intSet")
public static Object[][] intSet(){
return genericData(size -> IntStream.range(0, size).toArray(),
new IntBinaryOperator[]{
@@ -68,7 +68,7 @@
Integer::min});
}
- @DataProvider
+ @DataProvider(name = "longSet")
public static Object[][] longSet(){
return genericData(size -> LongStream.range(0, size).toArray(),
new LongBinaryOperator[]{
@@ -76,7 +76,7 @@
Long::min});
}
- @DataProvider
+ @DataProvider(name = "doubleSet")
public static Object[][] doubleSet(){
return genericData(size -> IntStream.range(0, size).mapToDouble(i -> (double)i).toArray(),
new DoubleBinaryOperator[]{
@@ -84,7 +84,7 @@
Double::min});
}
- @DataProvider
+ @DataProvider(name = "stringSet")
public static Object[][] stringSet(){
Function<Integer, String[]> stringsFunc = size ->
IntStream.range(0, size).mapToObj(Integer::toString).toArray(String[]::new);
@@ -121,11 +121,11 @@
int[] parallelResult = data.clone();
Arrays.parallelPrefix(parallelResult, fromIndex, toIndex, op);
- assertEquals(parallelResult, sequentialResult);
+ assertArraysEqual(parallelResult, sequentialResult);
int[] parallelRangeResult = Arrays.copyOfRange(data, fromIndex, toIndex);
Arrays.parallelPrefix(parallelRangeResult, op);
- assertEquals(parallelRangeResult, Arrays.copyOfRange(sequentialResult, fromIndex, toIndex));
+ assertArraysEqual(parallelRangeResult, Arrays.copyOfRange(sequentialResult, fromIndex, toIndex));
}
@Test(dataProvider="longSet")
@@ -137,11 +137,11 @@
long[] parallelResult = data.clone();
Arrays.parallelPrefix(parallelResult, fromIndex, toIndex, op);
- assertEquals(parallelResult, sequentialResult);
+ assertArraysEqual(parallelResult, sequentialResult);
long[] parallelRangeResult = Arrays.copyOfRange(data, fromIndex, toIndex);
Arrays.parallelPrefix(parallelRangeResult, op);
- assertEquals(parallelRangeResult, Arrays.copyOfRange(sequentialResult, fromIndex, toIndex));
+ assertArraysEqual(parallelRangeResult, Arrays.copyOfRange(sequentialResult, fromIndex, toIndex));
}
@Test(dataProvider="doubleSet")
@@ -153,11 +153,11 @@
double[] parallelResult = data.clone();
Arrays.parallelPrefix(parallelResult, fromIndex, toIndex, op);
- assertEquals(parallelResult, sequentialResult);
+ assertArraysEqual(parallelResult, sequentialResult);
double[] parallelRangeResult = Arrays.copyOfRange(data, fromIndex, toIndex);
Arrays.parallelPrefix(parallelRangeResult, op);
- assertEquals(parallelRangeResult, Arrays.copyOfRange(sequentialResult, fromIndex, toIndex));
+ assertArraysEqual(parallelRangeResult, Arrays.copyOfRange(sequentialResult, fromIndex, toIndex));
}
@Test(dataProvider="stringSet")
@@ -169,11 +169,11 @@
String[] parallelResult = data.clone();
Arrays.parallelPrefix(parallelResult, fromIndex, toIndex, op);
- assertEquals(parallelResult, sequentialResult);
+ assertArraysEqual(parallelResult, sequentialResult);
String[] parallelRangeResult = Arrays.copyOfRange(data, fromIndex, toIndex);
Arrays.parallelPrefix(parallelRangeResult, op);
- assertEquals(parallelRangeResult, Arrays.copyOfRange(sequentialResult, fromIndex, toIndex));
+ assertArraysEqual(parallelRangeResult, Arrays.copyOfRange(sequentialResult, fromIndex, toIndex));
}
@Test
@@ -293,5 +293,41 @@
public static void assertInstance(Object actual, Class<?> expected, String message) {
assertTrue(expected.isInstance(actual), message);
}
+
+ static void assertArraysEqual(int[] actual, int[] expected) {
+ try {
+ assertEquals(actual, expected, "");
+ } catch (AssertionError x) {
+ throw new AssertionError(String.format("Expected:%s, actual:%s",
+ Arrays.toString(expected), Arrays.toString(actual)), x);
+ }
+ }
+
+ static void assertArraysEqual(long[] actual, long[] expected) {
+ try {
+ assertEquals(actual, expected, "");
+ } catch (AssertionError x) {
+ throw new AssertionError(String.format("Expected:%s, actual:%s",
+ Arrays.toString(expected), Arrays.toString(actual)), x);
+ }
+ }
+
+ static void assertArraysEqual(double[] actual, double[] expected) {
+ try {
+ assertEquals(actual, expected, "");
+ } catch (AssertionError x) {
+ throw new AssertionError(String.format("Expected:%s, actual:%s",
+ Arrays.toString(expected), Arrays.toString(actual)), x);
+ }
+ }
+
+ static void assertArraysEqual(String[] actual, String[] expected) {
+ try {
+ assertEquals(actual, expected, "");
+ } catch (AssertionError x) {
+ throw new AssertionError(String.format("Expected:%s, actual:%s",
+ Arrays.toString(expected), Arrays.toString(actual)), x);
+ }
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Collections/EnumerationAsIterator.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8072726
+ * @summary Tests for Enumeration-to-Iterator conversion.
+ * @run testng EnumerationAsIterator
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+
+import static org.testng.Assert.*;
+
+@Test
+public class EnumerationAsIterator {
+ static Object[] of(String description, Supplier<Enumeration<?>> s, Collection<?> exp) {
+ return new Object[]{description, s, exp};
+ }
+
+ static Object[] of(String description, Collection<?> c, Collection<?> exp) {
+ return of(description, () -> Collections.enumeration(c), exp);
+ }
+
+ /**
+ * A wrapper Enumeration that doesn't override the
+ * default method on Enumeration.
+ */
+ static <T> Enumeration<T> wrapInDefault(Enumeration<T> e) {
+ return new Enumeration<>() {
+ @Override
+ public boolean hasMoreElements() {
+ return e.hasMoreElements();
+ }
+
+ @Override
+ public T nextElement() {
+ return e.nextElement();
+ }
+ };
+ }
+
+ @DataProvider
+ public static Iterator<Object[]> unmodifiable() {
+ return Arrays.asList(
+ of("Default-wrapped ArrayList",
+ () -> wrapInDefault(
+ Collections.enumeration(new ArrayList<>(Arrays.asList("a")))),
+ Arrays.asList("a")),
+
+ of("Unmodifiable ArrayList",
+ Collections.unmodifiableList(new ArrayList<>(Arrays.asList("a"))),
+ Arrays.asList("a")),
+
+ of("Modifiable ArrayList",
+ new ArrayList<>(Arrays.asList("a")),
+ Arrays.asList("a"))
+ ).iterator();
+ }
+
+ @DataProvider
+ public static Iterator<Object[]> others() {
+ return Arrays.asList(
+ of("Default Collections.emptyEnumeration()",
+ () -> wrapInDefault(Collections.emptyEnumeration()),
+ Collections.emptyList()),
+
+ of("Collections.emptyEnumeration()",
+ Collections::emptyEnumeration,
+ Collections.emptyList()),
+
+ of("Collections.emptyList()",
+ Collections.emptyList(),
+ Collections.emptyList()),
+
+ of("Collections.singletonList()",
+ Collections.singletonList("a"),
+ Collections.singletonList("a")),
+
+ of("Arrays.asList(...)",
+ Arrays.asList("a", "b", "c"),
+ Arrays.asList("a", "b", "c"))
+ ).iterator();
+ }
+
+ @DataProvider
+ public static Iterator<Object[]> all() {
+ List<Object[]> all = new ArrayList<>();
+ unmodifiable().forEachRemaining(all::add);
+ others().forEachRemaining(all::add);
+ return all.iterator();
+ }
+
+ @Test(dataProvider = "all")
+ public void consumeByNext(String description, Supplier<Enumeration<?>> s, Collection<?> exp) {
+ Iterator<?> i = s.get().asIterator();
+ int count = 0;
+ while (i.hasNext()) {
+ assertTrue(i.hasNext());
+
+ i.next();
+ count++;
+ }
+ assertEquals(count, exp.size());
+
+ assertFalse(i.hasNext());
+
+ try {
+ i.next();
+ fail();
+ } catch (NoSuchElementException e) {
+ }
+ }
+
+ @Test(dataProvider = "all")
+ public void consumeByForEachRemaining(String description,
+ Supplier<Enumeration<?>> s,
+ Collection<?> exp) {
+ Iterator<?> i = s.get().asIterator();
+ AtomicInteger ai = new AtomicInteger();
+ i.forEachRemaining(e -> ai.getAndIncrement());
+ assertEquals(ai.get(), exp.size());
+ i.forEachRemaining(e -> ai.getAndIncrement());
+ assertEquals(ai.get(), exp.size());
+
+ assertFalse(i.hasNext());
+
+ try {
+ i.next();
+ fail();
+ } catch (NoSuchElementException e) {
+ }
+ }
+
+ @Test(dataProvider = "all")
+ public void consumeByNextThenForEachRemaining(String description,
+ Supplier<Enumeration<?>> s,
+ Collection<?> exp) {
+ Iterator<?> i = s.get().asIterator();
+ AtomicInteger ai = new AtomicInteger();
+ if (i.hasNext()) {
+ i.next();
+ ai.getAndIncrement();
+ }
+ i.forEachRemaining(e -> ai.getAndIncrement());
+ assertEquals(ai.get(), exp.size());
+ i.forEachRemaining(e -> ai.getAndIncrement());
+ assertEquals(ai.get(), exp.size());
+
+ assertFalse(i.hasNext());
+
+ try {
+ i.next();
+ fail();
+ } catch (NoSuchElementException e) {
+ }
+ }
+
+ @Test(dataProvider = "all")
+ public void contents(String description, Supplier<Enumeration<?>> s, Collection<?> exp) {
+ assertEquals(copy(s.get()), exp);
+ }
+
+ private List<?> copy(Enumeration<?> input) {
+ List<Object> output = new ArrayList<>();
+ input.asIterator().forEachRemaining(output::add);
+ return output;
+ }
+
+ @Test(dataProvider = "unmodifiable",
+ expectedExceptions=UnsupportedOperationException.class)
+ public void removeThrowsAfterAdvancingE(String description,
+ Supplier<Enumeration<?>> s,
+ Collection<?> exp) {
+ Enumeration<?> e = s.get();
+ e.nextElement();
+ e.asIterator().remove();
+ }
+
+ @Test(dataProvider = "unmodifiable",
+ expectedExceptions=UnsupportedOperationException.class)
+ public void removeThrowsAfterAdvancingI(String description,
+ Supplier<Enumeration<?>> s,
+ Collection<?> exp) {
+ Iterator<?> i = s.get().asIterator();
+ i.next();
+ i.remove();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/CipherSuite.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA
+ * @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
+ * @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA256
+ * @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
+ * @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
+ * @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA
+ * @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
+ * @run main/othervm CipherSuite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+ * @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ * @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_GCM_SHA256
+ * @run main/othervm CipherSuite TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
+ * @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
+ * @run main/othervm CipherSuite TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
+ * @run main/othervm CipherSuite TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
+ */
+
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Test common DTLS cipher suites.
+ */
+public class CipherSuite extends DTLSOverDatagram {
+
+ // use the specific cipher suite
+ volatile static String cipherSuite;
+
+ public static void main(String[] args) throws Exception {
+ cipherSuite = args[0];
+
+ CipherSuite testCase = new CipherSuite();
+ testCase.runTest(testCase);
+ }
+
+ @Override
+ SSLEngine createSSLEngine(boolean isClient) throws Exception {
+ SSLEngine engine = super.createSSLEngine(isClient);
+
+ if (isClient) {
+ engine.setEnabledCipherSuites(new String[]{cipherSuite});
+ }
+
+ return engine;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/ClientAuth.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm ClientAuth
+ */
+
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Test DTLS client authentication.
+ */
+public class ClientAuth extends DTLSOverDatagram {
+
+ public static void main(String[] args) throws Exception {
+ ClientAuth testCase = new ClientAuth();
+ testCase.runTest(testCase);
+ }
+
+ @Override
+ SSLEngine createSSLEngine(boolean isClient) throws Exception {
+ SSLEngine engine = super.createSSLEngine(isClient);
+
+ if (!isClient) {
+ engine.setNeedClientAuth(true);
+ }
+
+ return engine;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/DTLSOverDatagram.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,602 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @run main/othervm DTLSOverDatagram
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.net.*;
+import java.util.*;
+import java.security.*;
+import java.security.cert.*;
+import javax.net.ssl.*;
+import java.util.concurrent.*;
+
+import sun.misc.HexDumpEncoder;
+
+/**
+ * An example to show the way to use SSLEngine in datagram connections.
+ */
+public class DTLSOverDatagram {
+ private static int MAX_HANDSHAKE_LOOPS = 60;
+ private static int MAX_APP_READ_LOOPS = 10;
+
+ /*
+ * The following is to set up the keystores.
+ */
+ private static String pathToStores = "../etc";
+ private static String keyStoreFile = "keystore";
+ private static String trustStoreFile = "truststore";
+ private static String passwd = "passphrase";
+
+ private static String keyFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + keyStoreFile;
+ private static String trustFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + trustStoreFile;
+ private static Exception clientException = null;
+ private static Exception serverException = null;
+
+ private static ByteBuffer serverApp =
+ ByteBuffer.wrap("Hi Client, I'm Server".getBytes());
+ private static ByteBuffer clientApp =
+ ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
+
+ /*
+ * =============================================================
+ * The test case
+ */
+ public static void main(String[] args) throws Exception {
+ DTLSOverDatagram testCase = new DTLSOverDatagram();
+ testCase.runTest(testCase);
+ }
+
+ /*
+ * Define the server side of the test.
+ */
+ void doServerSide() throws Exception {
+ DatagramSocket socket = serverDatagramSocket;
+ socket.setSoTimeout(10000); // 10 second
+
+ // create SSLEngine
+ SSLEngine engine = createSSLEngine(false);
+
+ // handshaking
+ handshake(engine, socket, clientSocketAddr);
+
+ // read client application data
+ receiveAppData(engine, socket, clientApp);
+
+ // write server application data
+ deliverAppData(engine, socket, serverApp, clientSocketAddr);
+
+ socket.close();
+ }
+
+ /*
+ * Define the client side of the test.
+ */
+ void doClientSide() throws Exception {
+ DatagramSocket socket = clientDatagramSocket;
+ socket.setSoTimeout(1000); // 1 second read timeout
+
+ // create SSLEngine
+ SSLEngine engine = createSSLEngine(true);
+
+ // handshaking
+ handshake(engine, socket, serverSocketAddr);
+
+ // write client application data
+ deliverAppData(engine, socket, clientApp, serverSocketAddr);
+
+ // read server application data
+ receiveAppData(engine, socket, serverApp);
+ }
+
+ /*
+ * =============================================================
+ * The remainder is support stuff for DTLS operations.
+ */
+ SSLEngine createSSLEngine(boolean isClient) throws Exception {
+ SSLContext context = getDTLSContext();
+ SSLEngine engine = context.createSSLEngine();
+
+ SSLParameters paras = engine.getSSLParameters();
+ paras.setMaximumPacketSize(1024);
+
+ engine.setUseClientMode(isClient);
+ engine.setSSLParameters(paras);
+
+ return engine;
+ }
+
+ // handshake
+ void handshake(SSLEngine engine, DatagramSocket socket,
+ SocketAddress peerAddr) throws Exception {
+
+ boolean endLoops = false;
+ int loops = MAX_HANDSHAKE_LOOPS;
+ engine.beginHandshake();
+ while (!endLoops &&
+ (serverException == null) && (clientException == null)) {
+
+ if (--loops < 0) {
+ throw new RuntimeException(
+ "Too much loops to produce handshake packets");
+ }
+
+ SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
+ if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
+ hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
+
+ ByteBuffer iNet;
+ ByteBuffer iApp;
+ if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
+ // receive ClientHello request and other SSL/TLS records
+ byte[] buf = new byte[1024];
+ DatagramPacket packet = new DatagramPacket(buf, buf.length);
+ try {
+ socket.receive(packet);
+ } catch (SocketTimeoutException ste) {
+ List<DatagramPacket> packets =
+ onReceiveTimeout(engine, peerAddr);
+ for (DatagramPacket p : packets) {
+ socket.send(p);
+ }
+
+ continue;
+ }
+
+ iNet = ByteBuffer.wrap(buf, 0, packet.getLength());
+ iApp = ByteBuffer.allocate(1024);
+ } else {
+ iNet = ByteBuffer.allocate(0);
+ iApp = ByteBuffer.allocate(1024);
+ }
+
+ SSLEngineResult r = engine.unwrap(iNet, iApp);
+ SSLEngineResult.Status rs = r.getStatus();
+ hs = r.getHandshakeStatus();
+ if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+ // the client maximum fragment size config does not work?
+ throw new Exception("Buffer overflow: " +
+ "incorrect client maximum fragment size");
+ } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
+ // bad packet, or the client maximum fragment size
+ // config does not work?
+ if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+ throw new Exception("Buffer underflow: " +
+ "incorrect client maximum fragment size");
+ } // otherwise, ignore this packet
+ } else if (rs == SSLEngineResult.Status.CLOSED) {
+ endLoops = true;
+ } // otherwise, SSLEngineResult.Status.OK:
+
+ if (rs != SSLEngineResult.Status.OK) {
+ continue;
+ }
+ } else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
+ List<DatagramPacket> packets =
+ produceHandshakePackets(engine, peerAddr);
+ for (DatagramPacket p : packets) {
+ socket.send(p);
+ }
+ } else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+ runDelegatedTasks(engine);
+ } else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+ // OK, time to do application data exchange.
+ endLoops = true;
+ } else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
+ endLoops = true;
+ }
+ }
+
+ SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
+ if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+ throw new Exception("Not ready for application data yet");
+ }
+ }
+
+ // deliver application data
+ void deliverAppData(SSLEngine engine, DatagramSocket socket,
+ ByteBuffer appData, SocketAddress peerAddr) throws Exception {
+
+ // Note: have not consider the packet loses
+ List<DatagramPacket> packets =
+ produceApplicationPackets(engine, appData, peerAddr);
+ appData.flip();
+ for (DatagramPacket p : packets) {
+ socket.send(p);
+ }
+ }
+
+ // receive application data
+ void receiveAppData(SSLEngine engine,
+ DatagramSocket socket, ByteBuffer expectedApp) throws Exception {
+
+ int loops = MAX_APP_READ_LOOPS;
+ while ((serverException == null) && (clientException == null)) {
+ if (--loops < 0) {
+ throw new RuntimeException(
+ "Too much loops to receive application data");
+ }
+
+ byte[] buf = new byte[1024];
+ DatagramPacket packet = new DatagramPacket(buf, buf.length);
+ socket.receive(packet);
+ ByteBuffer netBuffer = ByteBuffer.wrap(buf, 0, packet.getLength());
+ ByteBuffer recBuffer = ByteBuffer.allocate(1024);
+ SSLEngineResult rs = engine.unwrap(netBuffer, recBuffer);
+ recBuffer.flip();
+ if (recBuffer.remaining() != 0) {
+ printHex("Received application data", recBuffer);
+ if (!recBuffer.equals(expectedApp)) {
+ System.out.println("Engine status is " + rs);
+ throw new Exception("Not the right application data");
+ }
+ break;
+ }
+ }
+ }
+
+ // produce handshake packets
+ List<DatagramPacket> produceHandshakePackets(
+ SSLEngine engine, SocketAddress socketAddr) throws Exception {
+
+ List<DatagramPacket> packets = new ArrayList<>();
+ boolean endLoops = false;
+ int loops = MAX_HANDSHAKE_LOOPS;
+ while (!endLoops &&
+ (serverException == null) && (clientException == null)) {
+
+ if (--loops < 0) {
+ throw new RuntimeException(
+ "Too much loops to produce handshake packets");
+ }
+
+ ByteBuffer oNet = ByteBuffer.allocate(32768);
+ ByteBuffer oApp = ByteBuffer.allocate(0);
+ SSLEngineResult r = engine.wrap(oApp, oNet);
+ oNet.flip();
+
+ SSLEngineResult.Status rs = r.getStatus();
+ SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus();
+ if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+ // the client maximum fragment size config does not work?
+ throw new Exception("Buffer overflow: " +
+ "incorrect server maximum fragment size");
+ } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
+ // bad packet, or the client maximum fragment size
+ // config does not work?
+ if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+ throw new Exception("Buffer underflow: " +
+ "incorrect server maximum fragment size");
+ } // otherwise, ignore this packet
+ } else if (rs == SSLEngineResult.Status.CLOSED) {
+ throw new Exception("SSLEngine has closed");
+ } // otherwise, SSLEngineResult.Status.OK
+
+ // SSLEngineResult.Status.OK:
+ if (oNet.hasRemaining()) {
+ byte[] ba = new byte[oNet.remaining()];
+ oNet.get(ba);
+ DatagramPacket packet = createHandshakePacket(ba, socketAddr);
+ packets.add(packet);
+ }
+ boolean endInnerLoop = false;
+ SSLEngineResult.HandshakeStatus nhs = hs;
+ while (!endInnerLoop) {
+ if (nhs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+ runDelegatedTasks(engine);
+ nhs = engine.getHandshakeStatus();
+ } else if ((nhs == SSLEngineResult.HandshakeStatus.FINISHED) ||
+ (nhs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) ||
+ (nhs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
+
+ endInnerLoop = true;
+ endLoops = true;
+ } else if (nhs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
+ endInnerLoop = true;
+ }
+ }
+ }
+
+ return packets;
+ }
+
+ DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
+ return new DatagramPacket(ba, ba.length, socketAddr);
+ }
+
+ // produce application packets
+ List<DatagramPacket> produceApplicationPackets(
+ SSLEngine engine, ByteBuffer source,
+ SocketAddress socketAddr) throws Exception {
+
+ List<DatagramPacket> packets = new ArrayList<>();
+ ByteBuffer appNet = ByteBuffer.allocate(32768);
+ SSLEngineResult r = engine.wrap(source, appNet);
+ appNet.flip();
+
+ SSLEngineResult.Status rs = r.getStatus();
+ if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+ // the client maximum fragment size config does not work?
+ throw new Exception("Buffer overflow: " +
+ "incorrect server maximum fragment size");
+ } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
+ // unlikely
+ throw new Exception("Buffer underflow during wraping");
+ } else if (rs == SSLEngineResult.Status.CLOSED) {
+ throw new Exception("SSLEngine has closed");
+ } // otherwise, SSLEngineResult.Status.OK
+
+ // SSLEngineResult.Status.OK:
+ if (appNet.hasRemaining()) {
+ byte[] ba = new byte[appNet.remaining()];
+ appNet.get(ba);
+ DatagramPacket packet =
+ new DatagramPacket(ba, ba.length, socketAddr);
+ packets.add(packet);
+ }
+
+ return packets;
+ }
+
+ // run delegated tasks
+ void runDelegatedTasks(SSLEngine engine) throws Exception {
+ Runnable runnable;
+ while ((runnable = engine.getDelegatedTask()) != null) {
+ runnable.run();
+ }
+
+ SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
+ if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+ throw new Exception("handshake shouldn't need additional tasks");
+ }
+ }
+
+ // retransmission if timeout
+ List<DatagramPacket> onReceiveTimeout(
+ SSLEngine engine, SocketAddress socketAddr) throws Exception {
+
+ SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
+ if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+ return new ArrayList<DatagramPacket>();
+ } else {
+ // retransmission of handshake messages
+ return produceHandshakePackets(engine, socketAddr);
+ }
+ }
+
+ // get DTSL context
+ SSLContext getDTLSContext() throws Exception {
+ KeyStore ks = KeyStore.getInstance("JKS");
+ KeyStore ts = KeyStore.getInstance("JKS");
+
+ char[] passphrase = "passphrase".toCharArray();
+
+ ks.load(new FileInputStream(keyFilename), passphrase);
+ ts.load(new FileInputStream(trustFilename), passphrase);
+
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(ks, passphrase);
+
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+ tmf.init(ts);
+
+ SSLContext sslCtx = SSLContext.getInstance("DTLS");
+
+ sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+ return sslCtx;
+ }
+
+
+ /*
+ * =============================================================
+ * The remainder is support stuff to kickstart the testing.
+ */
+
+ // the server side SocketAddress
+ volatile static SocketAddress serverSocketAddr = null;
+
+ // the client side SocketAddress
+ volatile static SocketAddress clientSocketAddr = null;
+
+ // the server side DatagramSocket instance
+ volatile static DatagramSocket serverDatagramSocket = null;
+
+ // the server side DatagramSocket instance
+ volatile static DatagramSocket clientDatagramSocket = null;
+
+ // get server side SocketAddress object
+ public SocketAddress getServerSocketAddress() {
+ return serverSocketAddr;
+ }
+
+ // get client side SocketAddress object
+ public SocketAddress getClientSocketAddress() {
+ return clientSocketAddr;
+ }
+
+ // get server side DatagramSocket object
+ public DatagramSocket getServerDatagramSocket() {
+ return serverDatagramSocket;
+ }
+
+ // get client side DatagramSocket object
+ public DatagramSocket getClientDatagramSocket() {
+ return clientDatagramSocket;
+ }
+
+ // Will the handshaking and application data exchange succeed?
+ public boolean isGoodJob() {
+ return true;
+ }
+
+ public final void runTest(DTLSOverDatagram testCase) throws Exception {
+ serverDatagramSocket = new DatagramSocket();
+ serverSocketAddr = new InetSocketAddress(
+ InetAddress.getLocalHost(), serverDatagramSocket.getLocalPort());
+
+ clientDatagramSocket = new DatagramSocket();
+ clientSocketAddr = new InetSocketAddress(
+ InetAddress.getLocalHost(), clientDatagramSocket.getLocalPort());
+
+ ExecutorService pool = Executors.newFixedThreadPool(2);
+ List<Future<String>> list = new ArrayList<Future<String>>();
+
+ try {
+ list.add(pool.submit(new ServerCallable(testCase))); // server task
+ list.add(pool.submit(new ClientCallable(testCase))); // client task
+ } finally {
+ pool.shutdown();
+ }
+
+ Exception reserved = null;
+ for (Future<String> fut : list) {
+ try {
+ System.out.println(fut.get());
+ } catch (CancellationException |
+ InterruptedException | ExecutionException cie) {
+ if (reserved != null) {
+ cie.addSuppressed(reserved);
+ reserved = cie;
+ } else {
+ reserved = cie;
+ }
+ }
+ }
+
+ if (reserved != null) {
+ throw reserved;
+ }
+ }
+
+ final static class ServerCallable implements Callable<String> {
+ DTLSOverDatagram testCase;
+
+ ServerCallable(DTLSOverDatagram testCase) {
+ this.testCase = testCase;
+ }
+
+ @Override
+ public String call() throws Exception {
+ try {
+ testCase.doServerSide();
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ serverException = e;
+
+ if (testCase.isGoodJob()) {
+ throw e;
+ } else {
+ return "Well done, server!";
+ }
+ } finally {
+ if (serverDatagramSocket != null) {
+ serverDatagramSocket.close();
+ }
+ }
+
+ if (testCase.isGoodJob()) {
+ return "Well done, server!";
+ } else {
+ throw new Exception("No expected exception");
+ }
+ }
+ }
+
+ final static class ClientCallable implements Callable<String> {
+ DTLSOverDatagram testCase;
+
+ ClientCallable(DTLSOverDatagram testCase) {
+ this.testCase = testCase;
+ }
+
+ @Override
+ public String call() throws Exception {
+ try {
+ testCase.doClientSide();
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ clientException = e;
+ if (testCase.isGoodJob()) {
+ throw e;
+ } else {
+ return "Well done, client!";
+ }
+ } finally {
+ if (clientDatagramSocket != null) {
+ clientDatagramSocket.close();
+ }
+ }
+
+ if (testCase.isGoodJob()) {
+ return "Well done, client!";
+ } else {
+ throw new Exception("No expected exception");
+ }
+ }
+ }
+
+ final static void printHex(String prefix, ByteBuffer bb) {
+ HexDumpEncoder dump = new HexDumpEncoder();
+
+ synchronized (System.out) {
+ System.out.println(prefix);
+ try {
+ dump.encodeBuffer(bb.slice(), System.out);
+ } catch (Exception e) {
+ // ignore
+ }
+ System.out.flush();
+ }
+ }
+
+ final static void printHex(String prefix,
+ byte[] bytes, int offset, int length) {
+
+ HexDumpEncoder dump = new HexDumpEncoder();
+
+ synchronized (System.out) {
+ System.out.println(prefix);
+ try {
+ ByteBuffer bb = ByteBuffer.wrap(bytes, offset, length);
+ dump.encodeBuffer(bb, System.out);
+ } catch (Exception e) {
+ // ignore
+ }
+ System.out.flush();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/InvalidCookie.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm InvalidCookie
+ */
+
+import java.net.DatagramPacket;
+import java.net.SocketAddress;
+
+/**
+ * Test that if the handshake cookie in client side is incorrect, the handshake
+ * process can continue as if the client does not use cookie.
+ */
+public class InvalidCookie extends DTLSOverDatagram {
+ boolean needInvalidCookie = true;
+
+ public static void main(String[] args) throws Exception {
+ InvalidCookie testCase = new InvalidCookie();
+ testCase.runTest(testCase);
+ }
+
+ @Override
+ DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
+ if (needInvalidCookie && (ba.length >= 60) &&
+ (ba[0] == (byte)0x16) && (ba[13] == (byte)0x03)) {
+ // HelloVerifyRequest
+ needInvalidCookie = false;
+ System.out.println("invalidate handshake verify cookie");
+ if (ba[ba.length - 1] == (byte)0xFF) {
+ ba[ba.length - 1] = (byte)0xFE;
+ } else {
+ ba[ba.length - 1] = (byte)0xFF;
+ }
+ }
+
+ return super.createHandshakePacket(ba, socketAddr);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/InvalidRecords.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm InvalidRecords
+ */
+
+import java.net.DatagramPacket;
+import java.net.SocketAddress;
+
+/**
+ * Test that if handshake messages are crasged, the handshake would fail
+ * because of handshaking hash verification.
+ */
+public class InvalidRecords extends DTLSOverDatagram {
+ boolean needInvalidRecords = true;
+
+ public static void main(String[] args) throws Exception {
+ InvalidRecords testCase = new InvalidRecords();
+ testCase.runTest(testCase);
+ }
+
+ @Override
+ public boolean isGoodJob() {
+ return false;
+ }
+
+ @Override
+ DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
+ if (needInvalidRecords && (ba.length >= 60) &&
+ (ba[0x00] == (byte)0x16) && (ba[0x0D] == (byte)0x01) &&
+ (ba[0x3B] == (byte)0x00) && (ba[0x3C] > 0)) {
+
+ // ba[0x00]: record type
+ // ba[0x0D]: handshake type
+ // ba[0x3B]: length of session ID
+ // ba[0x3C]: length of cookie
+
+ // ClientHello with cookie
+ needInvalidRecords = false;
+ System.out.println("invalidate ClientHello message");
+ if (ba[ba.length - 1] == (byte)0xFF) {
+ ba[ba.length - 1] = (byte)0xFE;
+ } else {
+ ba[ba.length - 1] = (byte)0xFF;
+ }
+ }
+
+ return super.createHandshakePacket(ba, socketAddr);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/NoMacInitialClientHello.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm NoMacInitialClientHello
+ */
+
+import java.net.DatagramPacket;
+import java.net.SocketAddress;
+
+/**
+ * Test that a server is able to discard invalid initial ClientHello silently.
+ */
+public class NoMacInitialClientHello extends DTLSOverDatagram {
+ boolean needInvalidRecords = true;
+
+ public static void main(String[] args) throws Exception {
+ NoMacInitialClientHello testCase = new NoMacInitialClientHello();
+ testCase.runTest(testCase);
+ }
+
+ @Override
+ DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
+ if (needInvalidRecords && (ba.length >= 60) &&
+ (ba[0] == (byte)0x16) && (ba[13] == (byte)0x01)) { // ClientHello
+
+ needInvalidRecords = false;
+ System.out.println("invalidate ClientHello message");
+ if (ba[ba.length - 1] == (byte)0xFF) {
+ ba[ba.length - 1] = (byte)0xFE;
+ } else {
+ ba[ba.length - 1] = (byte)0xFF;
+ }
+ }
+
+ return super.createHandshakePacket(ba, socketAddr);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/Reordered.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm Reordered
+ */
+
+import java.util.List;
+import java.util.Collections;
+import java.net.DatagramPacket;
+import java.net.SocketAddress;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Test that DTLS implementation is able to reorder data traffic.
+ */
+public class Reordered extends DTLSOverDatagram {
+ boolean needPacketReorder = true;
+
+ public static void main(String[] args) throws Exception {
+ Reordered testCase = new Reordered();
+ testCase.runTest(testCase);
+ }
+
+ @Override
+ List<DatagramPacket> produceHandshakePackets(
+ SSLEngine engine, SocketAddress socketAddr) throws Exception {
+ List<DatagramPacket> packets =
+ super.produceHandshakePackets(engine, socketAddr);
+
+ if (needPacketReorder && (!engine.getUseClientMode())) {
+ needPacketReorder = false;
+ Collections.reverse(packets);
+ }
+
+ return packets;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/Retransmission.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm Retransmission
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+import java.net.DatagramPacket;
+import java.net.SocketAddress;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Test that DTLS implementation is able to do retransmission internally
+ * automatically if packet get lost.
+ */
+public class Retransmission extends DTLSOverDatagram {
+ boolean needPacketLoss = true;
+
+ public static void main(String[] args) throws Exception {
+ Retransmission testCase = new Retransmission();
+ testCase.runTest(testCase);
+ }
+
+ @Override
+ List<DatagramPacket> produceHandshakePackets(
+ SSLEngine engine, SocketAddress socketAddr) throws Exception {
+ List<DatagramPacket> packets =
+ super.produceHandshakePackets(engine, socketAddr);
+
+ if (!needPacketLoss || (!engine.getUseClientMode())) {
+ return packets;
+ }
+
+ List<DatagramPacket> parts = new ArrayList<>();
+ int lostSeq = 2;
+ for (DatagramPacket packet : packets) {
+ lostSeq--;
+ if (lostSeq == 0) {
+ needPacketLoss = false;
+ // loss this packet
+ System.out.println("Loss a packet");
+ continue;
+ }
+
+ parts.add(packet);
+ }
+
+ return parts;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/DTLS/WeakCipherSuite.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8043758
+ * @summary Datagram Transport Layer Security (DTLS)
+ * @compile DTLSOverDatagram.java
+ * @run main/othervm WeakCipherSuite TLS_DH_anon_WITH_AES_128_GCM_SHA256
+ * @run main/othervm WeakCipherSuite SSL_DH_anon_WITH_DES_CBC_SHA
+ * @run main/othervm WeakCipherSuite SSL_RSA_WITH_DES_CBC_SHA
+ * @run main/othervm WeakCipherSuite SSL_DHE_RSA_WITH_DES_CBC_SHA
+ * @run main/othervm WeakCipherSuite SSL_DHE_DSS_WITH_DES_CBC_SHA
+ */
+
+import javax.net.ssl.SSLEngine;
+import java.security.Security;
+
+/**
+ * Test common DTLS weak cipher suites.
+ */
+public class WeakCipherSuite extends DTLSOverDatagram {
+
+ // use the specific cipher suite
+ volatile static String cipherSuite;
+
+ public static void main(String[] args) throws Exception {
+ // reset security properties to make sure that the algorithms
+ // and keys used in this test are not disabled.
+ Security.setProperty("jdk.tls.disabledAlgorithms", "");
+ Security.setProperty("jdk.certpath.disabledAlgorithms", "");
+
+ cipherSuite = args[0];
+
+ WeakCipherSuite testCase = new WeakCipherSuite();
+ testCase.runTest(testCase);
+ }
+
+ @Override
+ SSLEngine createSSLEngine(boolean isClient) throws Exception {
+ SSLEngine engine = super.createSSLEngine(isClient);
+ engine.setEnabledCipherSuites(new String[]{cipherSuite});
+
+ return engine;
+ }
+}
--- a/jdk/test/javax/net/ssl/SSLEngine/CheckStatus.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/javax/net/ssl/SSLEngine/CheckStatus.java Thu Jun 04 18:49:37 2015 -0700
@@ -391,7 +391,7 @@
appOut2.rewind();
log("======================================");
- log("Client Cert");
+ log("Client Cert and Key Exchange");
result1 = ssle1.wrap(appOut1, oneToTwo);
checkResult(appOut1, oneToTwo, result1,
Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
@@ -406,22 +406,6 @@
oneToTwo.compact();
log("======================================");
- log("Key Exchange");
- result1 = ssle1.wrap(appOut1, oneToTwo);
- checkResult(appOut1, oneToTwo, result1,
- Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
-
- oneToTwo.flip();
- result2 = ssle2.unwrap(oneToTwo, appIn2);
-
- checkResult(oneToTwo, appIn2, result2,
- Status.OK, HandshakeStatus.NEED_TASK,
- result1.bytesProduced(), 0);
- runDelegatedTasks(ssle2);
-
- oneToTwo.compact();
-
- log("======================================");
log("CCS");
result1 = ssle1.wrap(appOut1, oneToTwo);
checkResult(appOut1, oneToTwo, result1,
--- a/jdk/test/javax/net/ssl/SSLEngine/LargeBufs.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/javax/net/ssl/SSLEngine/LargeBufs.java Thu Jun 04 18:49:37 2015 -0700
@@ -116,7 +116,7 @@
if ((result2.bytesConsumed() != 0) &&
(result2.bytesConsumed() != appBufferMax) &&
(result2.bytesConsumed() != 2 * OFFSET)) {
- throw new Exception("result1: " + result1);
+ throw new Exception("result2: " + result2);
}
log("wrap1: " + result1);
--- a/jdk/test/javax/net/ssl/TLS/CipherTestUtils.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/javax/net/ssl/TLS/CipherTestUtils.java Thu Jun 04 18:49:37 2015 -0700
@@ -406,6 +406,7 @@
return params;
}).forEach((params) -> {
try {
+ System.out.println("Testing " + params);
runTest(params);
System.out.println("Passed " + params);
} catch (Exception e) {
--- a/jdk/test/javax/net/ssl/TLSv11/ExportableBlockCipher.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/javax/net/ssl/TLSv11/ExportableBlockCipher.java Thu Jun 04 18:49:37 2015 -0700
@@ -23,15 +23,16 @@
* questions.
*/
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
/*
* @test
* @bug 4873188
* @summary Support TLS 1.1
* @run main/othervm ExportableBlockCipher
- *
- * SunJSSE does not support dynamic system properties, no way to re-use
- * system properties in samevm/agentvm mode.
- *
* @author Xuelei Fan
*/
@@ -109,7 +110,7 @@
sslIS.read();
sslOS.write('A');
sslOS.flush();
- } catch (SSLException ssle) {
+ } catch (IOException ioe) {
// get the expected exception
interrupted = true;
} finally {
--- a/jdk/test/javax/net/ssl/TLSv11/ExportableStreamCipher.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/javax/net/ssl/TLSv11/ExportableStreamCipher.java Thu Jun 04 18:49:37 2015 -0700
@@ -23,15 +23,16 @@
* questions.
*/
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
/*
* @test
* @bug 4873188
* @summary Support TLS 1.1
* @run main/othervm ExportableStreamCipher
- *
- * SunJSSE does not support dynamic system properties, no way to re-use
- * system properties in samevm/agentvm mode.
- *
* @author Xuelei Fan
*/
@@ -109,7 +110,7 @@
sslIS.read();
sslOS.write('A');
sslOS.flush();
- } catch (SSLException ssle) {
+ } catch (IOException ioe) {
// get the expected exception
interrupted = true;
} finally {
--- a/jdk/test/javax/net/ssl/templates/SSLSocketSSLEngineTemplate.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/javax/net/ssl/templates/SSLSocketSSLEngineTemplate.java Thu Jun 04 18:49:37 2015 -0700
@@ -139,6 +139,11 @@
* Main entry point for this test.
*/
public static void main(String args[]) throws Exception {
+ // reset security properties to make sure that the algorithms
+ // and keys used in this test are not disabled.
+ Security.setProperty("jdk.tls.disabledAlgorithms", "");
+ Security.setProperty("jdk.certpath.disabledAlgorithms", "");
+
if (debug) {
System.setProperty("javax.net.debug", "all");
}
@@ -153,7 +158,12 @@
*/
SSLSocketSSLEngineTemplate test =
new SSLSocketSSLEngineTemplate(protocol);
+ log("-------------------------------------");
+ log("Testing " + protocol + " for direct buffers ...");
test.runTest(true);
+
+ log("---------------------------------------");
+ log("Testing " + protocol + " for indirect buffers ...");
test.runTest(false);
}
@@ -329,6 +339,10 @@
thread.join();
}
+ if (sslSocket != null) {
+ sslSocket.close();
+ }
+
if (serverException != null) {
if (clientException != null) {
serverException.initCause(clientException);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/print/attribute/URLPDFPrinting.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 4899773
+ * @summary Test for DocFlavor.URL.PDF support. No exception should be thrown.
+ * @run main URLPDFPrinting
+*/
+
+import java.awt.*;
+import javax.print.*;
+import javax.print.attribute.standard.*;
+import javax.print.attribute.*;
+import java.io.*;
+import java.util.Locale;
+import java.net.URL;
+
+public class URLPDFPrinting {
+ /**
+ * Constructor
+ */
+ public URLPDFPrinting() {
+ super();
+ }
+ /**
+ * Starts the application.
+ */
+ public static void main(java.lang.String[] args) {
+ URLPDFPrinting pd = new URLPDFPrinting();
+ PrintService service[] = null, defService = null;
+
+ service = PrintServiceLookup.lookupPrintServices(DocFlavor.URL.PDF, null);
+ if (service.length == 0) {
+ System.out.println("No PrintService support DocFlavor.URL.PDF");
+ return;
+ } else {
+ defService = service[0];
+ System.out.println("Print Service which supports DocFlavor.URL.PDF: "+defService);
+ }
+
+ System.out.println("is DocFlavor.URL.PDF supported? "+defService.isDocFlavorSupported(DocFlavor.URL.PDF));
+ HashPrintRequestAttributeSet prSet = new HashPrintRequestAttributeSet();
+ prSet.add(new Destination(new File("./dest.prn").toURI()));
+
+ DocPrintJob pj = defService.createPrintJob();
+ PrintDocument prDoc = new PrintDocument();
+ try {
+ pj.print(prDoc, prSet);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+}
+
+class PrintDocument implements Doc {
+ InputStream fStream = null;
+ DocFlavor flavor = null;
+ HashDocAttributeSet docSet = new HashDocAttributeSet();
+ URL url = null;
+
+ public PrintDocument() {
+ try {
+ url = PrintDocument.class.getResource("hello.pdf");
+ try{ Thread.sleep(6000); }catch(Exception e){ e.printStackTrace();}
+ fStream = url.openStream();
+ System.out.println("URL input stream "+fStream);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ docSet.add(OrientationRequested.LANDSCAPE);
+ }
+
+ public DocAttributeSet getAttributes() {
+ System.out.println("getAttributes called");
+ return docSet;
+ }
+
+ public DocFlavor getDocFlavor() {
+ System.out.println("getDocFlavor called");
+ return DocFlavor.URL.PDF;
+ }
+
+ public Object getPrintData(){
+ System.out.println("getPrintData called");
+ return url;
+ }
+
+ public Reader getReaderForText() {
+ return null;
+ }
+
+ public InputStream getStreamForBytes() {
+ System.out.println("getStreamForBytes called");
+ return fStream;
+ }
+}
Binary file jdk/test/javax/print/attribute/hello.pdf has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/GroupLayout/8079640/bug8079640.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ @bug 8079640
+ @summary GroupLayout incorrect layout with large JTextArea
+ @author Semyon Sadetsky
+ */
+
+
+import javax.swing.*;
+import java.awt.*;
+
+public class bug8079640 {
+
+ private static JFrame frame;
+ private static JComponent comp2;
+
+ public static void main(String[] args) throws Exception {
+
+ try {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ frame = new JFrame("A Frame");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setUndecorated(true);
+ setup(frame);
+ frame.setVisible(true);
+ }
+ });
+
+ test();
+ System.out.println("ok");
+
+ } finally {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ frame.dispose();
+ }
+ });
+ }
+ }
+
+ private static void test() throws Exception {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ if(comp2.getLocation().getY() > frame.getHeight())
+ throw new RuntimeException("GroupLayout fails: comp2 is out of the window");
+ }
+ });
+ }
+
+
+ static void setup(JFrame frame) {
+ JPanel panel = new JPanel();
+ JComponent comp1 = new JLabel("Test Label 1");
+ comp1.setMinimumSize(new Dimension(1000, 40000));
+ comp1.setPreferredSize(new Dimension(1000, 40000));
+ JScrollPane scroll = new JScrollPane(comp1);
+ comp2 = new JLabel("Test Label 2");
+ GroupLayout layout = new GroupLayout(panel);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addComponent(scroll)
+ .addComponent(comp2));
+ layout.setVerticalGroup(
+ layout.createSequentialGroup()
+ .addComponent(scroll)
+ .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(comp2));
+ panel.setLayout(layout);
+ frame.getContentPane().add(panel, BorderLayout.CENTER);
+ frame.setSize(800, 600);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JComboBox/8033069/bug8033069NoScrollBar.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.AWTException;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.Point;
+import java.awt.Robot;
+import java.awt.event.InputEvent;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.UIManager.LookAndFeelInfo;
+import javax.swing.UnsupportedLookAndFeelException;
+
+/* @test
+ * @bug 8033069
+ * @summary Checks that JComboBox popup does not close when mouse wheel is
+ * rotated over combo box and over its popup. The case where popup
+ * has no scroll bar.
+ * @library ../../regtesthelpers
+ * @build Util
+ * @run main bug8033069NoScrollBar
+ * @author Alexey Ivanov
+ */
+public class bug8033069NoScrollBar implements Runnable {
+
+ private static final String[] NO_SCROLL_ITEMS = new String[] {
+ "A", "B", "C", "D", "E", "F"
+ };
+
+ private final Robot robot;
+
+ private final String[] items;
+
+ private JFrame frame;
+ private JComboBox cb1;
+ private JComboBox cb2;
+
+ public static void main(String[] args) throws Exception {
+ iterateLookAndFeels(new bug8033069NoScrollBar(NO_SCROLL_ITEMS));
+ }
+
+ protected static void iterateLookAndFeels(final bug8033069NoScrollBar test) throws Exception {
+ LookAndFeelInfo[] lafInfo = UIManager.getInstalledLookAndFeels();
+ for (LookAndFeelInfo info : lafInfo) {
+ try {
+ UIManager.setLookAndFeel(info.getClassName());
+ System.out.println("Look and Feel: " + info.getClassName());
+ test.runTest();
+ } catch (UnsupportedLookAndFeelException e) {
+ System.out.println("Skipping unsupported LaF: " + info);
+ }
+ }
+ }
+
+ public bug8033069NoScrollBar(String[] items) throws AWTException {
+ this.items = items;
+
+ robot = new Robot();
+ robot.setAutoDelay(200);
+ }
+
+ private void setupUI() {
+ frame = new JFrame();
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ cb1 = new JComboBox<>(items);
+ cb2 = new JComboBox<>(items);
+ JPanel panel = new JPanel(new GridLayout(1, 2));
+ panel.add(cb1);
+ panel.add(cb2);
+
+ frame.add(panel);
+
+ frame.pack();
+ frame.setVisible(true);
+ }
+
+ public void runTest() throws Exception {
+ try {
+ SwingUtilities.invokeAndWait(this);
+
+ robot.waitForIdle();
+ assertFalse("cb1 popup is visible",
+ Util.invokeOnEDT(cb1::isPopupVisible));
+
+ // Move mouse pointer to the center of the fist combo box
+ Point p = cb1.getLocationOnScreen();
+ Dimension d = cb1.getSize();
+ robot.mouseMove(p.x + d.width / 2, p.y + d.height / 2);
+ // Click it to open popup
+ robot.mousePress(InputEvent.BUTTON1_MASK);
+ robot.mouseRelease(InputEvent.BUTTON1_MASK);
+
+ robot.waitForIdle();
+ assertTrue("cb1 popup is not visible",
+ Util.invokeOnEDT(cb1::isPopupVisible));
+
+ robot.mouseWheel(1);
+ robot.waitForIdle();
+ assertTrue("cb1 popup is not visible after mouse wheel up on combo box",
+ Util.invokeOnEDT(cb1::isPopupVisible));
+
+ robot.mouseWheel(-1);
+ robot.waitForIdle();
+ assertTrue("cb1 popup is not visible after mouse wheel down on combo box",
+ Util.invokeOnEDT(cb1::isPopupVisible));
+
+ // Move mouse down on the popup
+ robot.mouseMove(p.x + d.width / 2, p.y + d.height * 3);
+
+ robot.mouseWheel(1);
+ robot.waitForIdle();
+ assertTrue("cb1 popup is not visible after mouse wheel up on popup",
+ Util.invokeOnEDT(cb1::isPopupVisible));
+
+ robot.mouseWheel(-1);
+ robot.waitForIdle();
+ assertTrue("cb1 popup is not visible after mouse wheel down on popup",
+ Util.invokeOnEDT(cb1::isPopupVisible));
+
+
+ // Move mouse pointer to the center of the second combo box
+ p = cb2.getLocationOnScreen();
+ d = cb2.getSize();
+ robot.mouseMove(p.x + d.width / 2, p.y + d.height / 2);
+
+ robot.mouseWheel(1);
+ robot.waitForIdle();
+ assertFalse("cb1 popup is visible after mouse wheel up on cb2",
+ Util.invokeOnEDT(cb1::isPopupVisible));
+ } finally {
+ if (frame != null) {
+ frame.dispose();
+ }
+ }
+
+ System.out.println("Test passed");
+ }
+
+ @Override
+ public void run() {
+ setupUI();
+ }
+
+ private static void assertTrue(String message, boolean value) {
+ assertEquals(message, true, value);
+ }
+
+ private static void assertFalse(String message, boolean value) {
+ assertEquals(message, false, value);
+ }
+
+ private static void assertEquals(String message, boolean expected, boolean actual) {
+ if (expected != actual) {
+ throw new RuntimeException(message);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JComboBox/8033069/bug8033069ScrollBar.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.AWTException;
+
+/* @test
+ * @bug 8033069
+ * @summary Checks that JComboBox popup does not close when mouse wheel is
+ * rotated over combo box and over its popup. The case where
+ * popup has scroll bar.
+ * @library ../../regtesthelpers
+ * @build Util
+ * @run main bug8033069ScrollBar
+ * @author Alexey Ivanov
+ */
+public class bug8033069ScrollBar extends bug8033069NoScrollBar {
+
+ private static final String[] SCROLL_ITEMS = new String[] {
+ "A", "B", "C", "D", "E", "F",
+ "G", "H", "I", "J", "K", "L",
+ "M", "N", "O", "P", "Q", "R"
+ };
+
+ public static void main(String[] args) throws Exception {
+ iterateLookAndFeels(new bug8033069ScrollBar(SCROLL_ITEMS));
+ }
+
+ public bug8033069ScrollBar(String[] items) throws AWTException {
+ super(items);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JFileChooser/8080628/bug8080628.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Locale;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.UIManager.LookAndFeelInfo;
+
+import sun.swing.SwingUtilities2;
+
+/*
+ * @test
+ * @bug 8080628
+ * @summary No mnemonics on Open and Save buttons in JFileChooser.
+ * @author Alexey Ivanov
+ * @run main bug8080628
+ */
+public class bug8080628 {
+ public static final String[] MNEMONIC_KEYS = new String[] {
+ "FileChooser.saveButtonMnemonic",
+ "FileChooser.openButtonMnemonic",
+ "FileChooser.cancelButtonMnemonic",
+ "FileChooser.directoryOpenButtonMnemonic"
+ };
+
+ public static final Locale[] LOCALES = new Locale[] {
+ new Locale("en"),
+ new Locale("de"),
+ new Locale("es"),
+ new Locale("fr"),
+ new Locale("it"),
+ new Locale("ja"),
+ new Locale("ko"),
+ new Locale("pt", "BR"),
+ new Locale("sv"),
+ new Locale("zh", "CN"),
+ new Locale("zh", "TW")
+ };
+
+ private static volatile Exception exception;
+
+ public static void main(String[] args) throws Exception {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ runTest();
+ }
+ });
+
+ if (exception != null) {
+ throw exception;
+ }
+ }
+
+ private static void runTest() {
+ try {
+ LookAndFeelInfo[] lafInfo = UIManager.getInstalledLookAndFeels();
+ for (LookAndFeelInfo info : lafInfo) {
+ UIManager.setLookAndFeel(info.getClassName());
+
+ for (Locale locale : LOCALES) {
+ for (String key : MNEMONIC_KEYS) {
+ int mnemonic = SwingUtilities2.getUIDefaultsInt(key, locale);
+ if (mnemonic != 0) {
+ throw new RuntimeException("No mnemonic expected (" + mnemonic + ") " +
+ "for '" + key + "' " +
+ "in locale '" + locale + "' " +
+ "in Look-and-Feel '"
+ + UIManager.getLookAndFeel().getClass().getName() + "'");
+ }
+ }
+ }
+ }
+ System.out.println("Test passed");
+ } catch (Exception e) {
+ exception = e;
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JRadioButton/8075609/bug8075609.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test
+ * @library ../../regtesthelpers
+ * @build Util
+ * @bug 8075609
+ * @summary IllegalArgumentException when transferring focus from JRadioButton using tab
+ * @author Vivi An
+ * @run main bug8075609
+ */
+
+import javax.swing.*;
+import javax.swing.event.*;
+import java.awt.event.*;
+import java.awt.*;
+import sun.awt.SunToolkit;
+
+public class bug8075609 {
+ private static Robot robot;
+ private static SunToolkit toolkit;
+ private static JTextField textField;
+
+ public static void main(String args[]) throws Throwable {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ createAndShowGUI();
+ }
+ });
+
+ robot = new Robot();
+ Thread.sleep(100);
+
+ robot.setAutoDelay(100);
+ toolkit = (SunToolkit) Toolkit.getDefaultToolkit();
+
+ // Radio button group tab key test
+ runTest1();
+ }
+
+ private static void createAndShowGUI() {
+ JFrame mainFrame = new JFrame("Bug 8075609 - 1 test");
+
+ JPanel rootPanel = new JPanel();
+ rootPanel.setLayout(new BorderLayout());
+
+ JPanel formPanel = new JPanel();
+ formPanel.setFocusTraversalPolicy(new LayoutFocusTraversalPolicy());
+ formPanel.setFocusCycleRoot(true);
+
+ JRadioButton option1 = new JRadioButton("Option 1", true);
+ JRadioButton option2 = new JRadioButton("Option 2");
+
+ ButtonGroup radioButtonGroup = new ButtonGroup();
+ radioButtonGroup.add(option1);
+ radioButtonGroup.add(option2);
+
+ formPanel.add(option1);
+ formPanel.add(option2);
+ textField = new JTextField("Another focusable component");
+ formPanel.add(textField);
+
+ rootPanel.add(formPanel, BorderLayout.CENTER);
+
+ JButton okButton = new JButton("OK");
+ rootPanel.add(okButton, BorderLayout.SOUTH);
+
+ mainFrame.add(rootPanel);
+ mainFrame.pack();
+ mainFrame.setVisible(true);
+ mainFrame.toFront();
+ }
+
+ // Radio button Group as a single component when traversing through tab key
+ private static void runTest1() throws Exception{
+ hitKey(robot, KeyEvent.VK_TAB);
+
+ robot.setAutoDelay(1000 );
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ if (textField.hasFocus()) {
+ System.out.println("Radio Button Group Go To Next Component through Tab Key failed");
+ throw new RuntimeException("Focus is not on textField as Expected");
+ }
+ }
+ });
+ }
+
+ private static void hitKey(Robot robot, int keycode) {
+ robot.keyPress(keycode);
+ robot.keyRelease(keycode);
+ toolkit.realSync();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JRootPane/SilenceOfDeprecatedMenuBar/SilenceOfDeprecatedMenuBar.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javax.swing.JFrame;
+import javax.swing.JMenuBar;
+import javax.swing.JRootPane;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+
+import static javax.swing.UIManager.getInstalledLookAndFeels;
+
+/**
+ * @test
+ * @bug 6368321
+ * @author Sergey Bylokhov
+ */
+public final class SilenceOfDeprecatedMenuBar implements Runnable {
+
+ public static void main(final String[] args) throws Exception {
+ for (final UIManager.LookAndFeelInfo laf : getInstalledLookAndFeels()) {
+ SwingUtilities.invokeAndWait(() -> setLookAndFeel(laf));
+ SwingUtilities.invokeAndWait(new SilenceOfDeprecatedMenuBar());
+ }
+ }
+
+ @Override
+ public void run() {
+ final JFrame frame = new DeprecatedFrame();
+ try {
+ final JMenuBar bar = new JMenuBar();
+ frame.setJMenuBar(bar);
+ frame.setBounds(100, 100, 100, 100);
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ if (bar != frame.getJMenuBar()) {
+ throw new RuntimeException("Wrong JMenuBar");
+ }
+ } finally {
+ frame.dispose();
+ }
+ }
+
+ private static void setLookAndFeel(final UIManager.LookAndFeelInfo laf) {
+ try {
+ UIManager.setLookAndFeel(laf.getClassName());
+ System.out.println("LookAndFeel: " + laf.getClassName());
+ } catch (ClassNotFoundException | InstantiationException |
+ UnsupportedLookAndFeelException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static class DeprecatedFrame extends JFrame {
+
+ @Override
+ protected JRootPane createRootPane() {
+ return new JRootPane() {
+ @Override
+ public JMenuBar getMenuBar() {
+ throw new RuntimeException("Should not be here");
+ }
+ @Override
+ public void setMenuBar(final JMenuBar menu) {
+ throw new RuntimeException("Should not be here");
+ }
+ };
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JSpinner/WrongEditorTextFieldFont/WrongEditorTextFieldFont.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.FlowLayout;
+import java.awt.Font;
+
+import javax.swing.JFrame;
+import javax.swing.JSpinner;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+import javax.swing.plaf.UIResource;
+
+import static javax.swing.JSpinner.*;
+import static javax.swing.UIManager.getInstalledLookAndFeels;
+
+/**
+ * @test
+ * @bug 5036022
+ * @author Sergey Bylokhov
+ */
+public class WrongEditorTextFieldFont implements Runnable {
+
+ private static final Font USERS_FONT = new Font("dialog", Font.BOLD, 41);
+
+ public static void main(final String[] args) throws Exception {
+ for (final UIManager.LookAndFeelInfo laf : getInstalledLookAndFeels()) {
+ SwingUtilities.invokeAndWait(() -> setLookAndFeel(laf));
+ SwingUtilities.invokeAndWait(new WrongEditorTextFieldFont());
+ }
+ }
+
+ @Override
+ public void run() {
+ final JFrame frame1 = new JFrame();
+ try {
+ testDefaultFont(frame1);
+ } finally {
+ frame1.dispose();
+ }
+ }
+
+ private static void testDefaultFont(final JFrame frame) {
+ final JSpinner spinner = new JSpinner();
+ final JSpinner spinner_u = new JSpinner();
+ frame.setLayout(new FlowLayout(FlowLayout.CENTER, 50, 50));
+ frame.getContentPane().add(spinner);
+ frame.getContentPane().add(spinner_u);
+ frame.pack();
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ final DefaultEditor ed = (DefaultEditor) spinner.getEditor();
+ final DefaultEditor ed_u = (DefaultEditor) spinner_u.getEditor();
+ ed_u.getTextField().setFont(USERS_FONT);
+
+ for (int i = 5; i < 40; i += 5) {
+ /*
+ * Validate that the font of the text field is changed to the
+ * font of JSpinner if the font of text field was not set by the
+ * user.
+ */
+ final Font tff = ed.getTextField().getFont();
+ if (!(tff instanceof UIResource)) {
+ throw new RuntimeException("Font must be UIResource");
+ }
+ if (spinner.getFont().getSize() != tff.getSize()) {
+ throw new RuntimeException("Rrong size");
+ }
+ spinner.setFont(new Font("dialog", Font.BOLD, i));
+ /*
+ * Validate that the font of the text field is NOT changed to the
+ * font of JSpinner if the font of text field was set by the user.
+ */
+ final Font tff_u = ed_u.getTextField().getFont();
+ if (tff_u instanceof UIResource || !tff_u.equals(USERS_FONT)) {
+ throw new RuntimeException("Font must NOT be UIResource");
+ }
+ if (spinner_u.getFont().getSize() == tff_u.getSize()) {
+ throw new RuntimeException("Wrong size");
+ }
+ spinner_u.setFont(new Font("dialog", Font.BOLD, i));
+ }
+ }
+
+ private static void setLookAndFeel(final UIManager.LookAndFeelInfo laf) {
+ try {
+ UIManager.setLookAndFeel(laf.getClassName());
+ System.out.println("LookAndFeel: " + laf.getClassName());
+ } catch (ClassNotFoundException | InstantiationException |
+ UnsupportedLookAndFeelException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JTextArea/TextViewOOM/TextViewOOM.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.EventQueue;
+
+import javax.swing.JFrame;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+
+/**
+ * @test
+ * @bug 8072775
+ * @run main/othervm -Xmx80m TextViewOOM
+ */
+public class TextViewOOM {
+
+ private static JFrame frame;
+ private static JTextArea ta;
+ private static final String STRING = "\uDC00\uD802\uDFFF";
+ private static final int N = 5000;
+
+ private static void createAndShowGUI() {
+ frame = new JFrame();
+ final JScrollPane jScrollPane1 = new JScrollPane();
+ ta = new JTextArea();
+
+ ta.setEditable(false);
+ ta.setColumns(20);
+ ta.setRows(5);
+ jScrollPane1.setViewportView(ta);
+ frame.add(ta);
+
+ frame.pack();
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public static void main(final String[] args) throws Exception {
+ /* Create and display the form */
+ EventQueue.invokeAndWait(TextViewOOM::createAndShowGUI);
+ for (int i = 0; i < 10; i++) {
+ System.gc();
+ Thread.sleep(1000);
+ }
+ long mem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+ System.err.println("Memory before creating the text: "+mem);
+ final StringBuilder sb = new StringBuilder(N * STRING.length());
+ for (int i = 0; i < N; i++) {
+ sb.append(STRING);
+ }
+ for (int i = 0; i < 10; i++) {
+ System.gc();
+ Thread.sleep(1000);
+ }
+ mem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+ System.err.println("Memory after creating the text: "+mem);
+
+ EventQueue.invokeAndWait(() -> {
+ ta.setText(sb.toString());
+ for (int i = 0; i < 10; i++) {
+ System.gc();
+ try {Thread.sleep(200);} catch (InterruptedException iex) {}
+ }
+ long mem1 = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+ System.err.println("Memory after setting the text: " + mem1);
+ });
+ for (int i = 0; i < 10; i++) {
+ System.gc();
+ Thread.sleep(1000);
+ }
+ mem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+ System.err.println("Final memory after everything: " + mem);
+ EventQueue.invokeAndWait(frame::dispose);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/RepaintManager/DisplayListenerLeak/DisplayListenerLeak.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.GraphicsEnvironment;
+
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+
+import sun.java2d.SunGraphicsEnvironment;
+
+/**
+ * @test
+ * @bug 8041654
+ * @run main/othervm -Xmx80m DisplayListenerLeak
+ */
+public final class DisplayListenerLeak {
+
+ private static JFrame frame;
+ private volatile static boolean failed = false;
+
+ private static void createAndShowGUI() {
+ Thread.currentThread().setUncaughtExceptionHandler((t, e) -> {
+ e.printStackTrace();
+ failed = true;
+ });
+ frame = new JFrame();
+ JLabel emptyLabel = new JLabel("");
+ emptyLabel.setPreferredSize(new Dimension(600, 400));
+ frame.getContentPane().add(emptyLabel, BorderLayout.CENTER);
+ frame.pack();
+ frame.setVisible(true);
+ }
+
+ public static void main(final String[] args) throws Exception {
+ GraphicsEnvironment ge =
+ GraphicsEnvironment.getLocalGraphicsEnvironment();
+ if (!(ge instanceof SunGraphicsEnvironment)) {
+ return;
+ }
+ EventQueue.invokeAndWait(() -> createAndShowGUI());
+ SunGraphicsEnvironment sge = (SunGraphicsEnvironment) ge;
+ final long startTime = System.nanoTime();
+ while (!failed) {
+ if (System.nanoTime() - startTime > 60_000_000_000L) {
+ break;
+ }
+ System.gc(); // clear all weak references
+ EventQueue.invokeAndWait(() -> {
+ frame.setSize(frame.getHeight(), frame.getWidth());
+ frame.pack();
+ });
+ EventQueue.invokeAndWait(sge::displayChanged);
+ }
+ EventQueue.invokeAndWait(frame::dispose);
+ if (failed) {
+ throw new RuntimeException();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/plaf/basic/BasicComboPopup/7072653/bug7072653.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ @bug 7072653
+ @summary JComboBox popup mispositioned if its height exceeds the screen height
+ @author Semyon Sadetsky
+ */
+
+
+import javax.swing.*;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import java.awt.*;
+import java.awt.Toolkit;
+
+public class bug7072653 {
+
+ private static JComboBox combobox;
+ private static JFrame frame;
+
+ public static void main(String[] args) throws Exception {
+ try {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ frame = new JFrame("JComboBox Test");
+ setup(frame);
+ }
+ });
+ test();
+ }
+ finally {
+ frame.dispose();
+ }
+
+ }
+
+ static void setup(JFrame frame) {
+
+
+ frame.setUndecorated(true);
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ frame.setSize(320, 200);
+
+ frame.getContentPane().setLayout(new FlowLayout());
+
+ combobox = new JComboBox(new DefaultComboBoxModel() {
+ public Object getElementAt(int index) { return "Element " + index; }
+ public int getSize() {
+ return 1000;
+ }
+ });
+
+
+ combobox.setMaximumRowCount(100);
+ frame.getContentPane().add(combobox);
+
+ frame.setVisible(true);
+
+ }
+
+ static void test() throws Exception{
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ combobox.addPopupMenuListener(new PopupMenuListener() {
+ @Override
+ public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
+ }
+
+ @Override
+ public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
+ int height = 0;
+ for (Window window : JFrame.getWindows()) {
+ if (Window.Type.POPUP == window.getType()) {
+ height = window.getSize().height;
+ break;
+ }
+ }
+ GraphicsConfiguration gc =
+ combobox.getGraphicsConfiguration();
+ Insets screenInsets = Toolkit.getDefaultToolkit()
+ .getScreenInsets(gc);
+
+ if (height == gc.getBounds().height - screenInsets.top -
+ screenInsets.bottom ) {
+ System.out.println("ok");
+ return;
+ }
+ throw new RuntimeException(
+ "Popup window height is wrong " + height);
+ }
+
+ @Override
+ public void popupMenuCanceled(PopupMenuEvent e) {
+ }
+ });
+ combobox.setPopupVisible(true);
+ combobox.setPopupVisible(false);
+ }
+ });
+ }
+
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/plaf/basic/BasicLabelUI/bug7172652.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ @bug 7172652
+ @summary With JDK 1.7 text field does not obtain focus when using mnemonic Alt/Key combin
+ @author Semyon Sadetsky
+ @library /lib/testlibrary
+ @build jdk.testlibrary.OSInfo
+ @run main bug7172652
+ */
+
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import java.awt.*;
+import java.awt.event.KeyEvent;
+import jdk.testlibrary.OSInfo;
+
+public class bug7172652 {
+
+ private static JMenu menu;
+ private static JFrame frame;
+ private static Boolean selected;
+
+ public static void main(String[] args) throws Exception {
+ if (OSInfo.getOSType() != OSInfo.OSType.WINDOWS) {
+ System.out.println("ok");
+ return;
+ }
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ setup();
+ }
+ });
+
+ test();
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ frame.dispose();
+ }
+ });
+ }
+
+ private static void test() throws Exception {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ menu.getModel().addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ selected = menu.isSelected();
+ }
+ });
+ }
+ });
+
+ Robot robot = new Robot();
+ robot.setAutoDelay(200);
+
+ robot.keyPress(KeyEvent.VK_ALT);
+ robot.keyPress(KeyEvent.VK_F);
+ robot.keyRelease(KeyEvent.VK_F);
+ robot.keyRelease(KeyEvent.VK_ALT);
+
+ robot.waitForIdle();
+ if( selected != null ) {
+ throw new RuntimeException("Menu is notified selected= " + selected);
+ }
+
+ robot.keyPress(KeyEvent.VK_ALT);
+ robot.keyPress(KeyEvent.VK_F);
+ robot.keyRelease(KeyEvent.VK_F);
+ robot.keyRelease(KeyEvent.VK_ALT);
+ if( selected != null ) {
+ throw new RuntimeException("Menu is notified selected= " + selected);
+ }
+
+ robot.waitForIdle();
+
+ robot.keyPress(KeyEvent.VK_ALT);
+ robot.keyPress(KeyEvent.VK_F);
+ robot.keyRelease(KeyEvent.VK_F);
+ robot.keyRelease(KeyEvent.VK_ALT);
+ if( selected != null ) {
+ throw new RuntimeException("Menu is notified selected= " + selected);
+ }
+
+ robot.waitForIdle();
+
+ robot.keyPress(KeyEvent.VK_ALT);
+ robot.keyPress(KeyEvent.VK_F);
+ robot.keyRelease(KeyEvent.VK_F);
+ robot.keyRelease(KeyEvent.VK_ALT);
+ if( selected != null ) {
+ throw new RuntimeException("Menu is notified selected= " + selected);
+ }
+
+ robot.waitForIdle();
+
+ System.out.printf("ok");
+ }
+
+ private static void setup() {
+ JLabel firstLbl = new JLabel("First name");
+ JLabel lastLbl = new JLabel("Last name");
+ JMenuBar menuBar = new JMenuBar();
+
+ JTextField firstTxtFld = new JTextField(20);
+ JTextField lastTxtFld = new JTextField(20);
+ JDesktopPane desktopPane = new JDesktopPane();
+ JInternalFrame iframe = new JInternalFrame("A frame", true, true, true, true);
+
+ // Set an initial size
+ iframe.setSize(200, 220);
+
+ // By default, internal frames are not visible; make it visible
+ iframe.setVisible(true);
+
+ JPanel pane = new JPanel();
+ pane.setLayout(new FlowLayout());
+
+ pane.add(firstLbl);
+ pane.add(firstTxtFld);
+ pane.add(lastLbl);
+ pane.add(lastTxtFld);
+
+ firstLbl.setLabelFor(firstTxtFld);
+ firstLbl.setDisplayedMnemonic('F');
+
+ lastLbl.setLabelFor(lastTxtFld);
+ lastLbl.setDisplayedMnemonic('L');
+
+ iframe.getContentPane().add(pane);
+ iframe.setJMenuBar(menuBar);
+ menu = new JMenu("FirstMenu");
+ //m.setMnemonic('i');
+ menuBar.add(menu);
+ desktopPane.add(iframe);
+
+ frame = new JFrame();
+ frame.setUndecorated(true);
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.getContentPane().add(desktopPane);
+ frame.setSize(300, 300);
+ frame.setVisible(true);
+ }
+
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/plaf/basic/BasicTextUI/8001470/bug8001470.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ @bug 8001470
+ @summary JTextField's size is computed incorrectly when it contains Indic or Thai characters
+ @author Semyon Sadetsky
+ */
+
+import javax.swing.*;
+import java.awt.*;
+
+public class bug8001470 {
+
+ private static JFrame frame;
+ private static JTextField textField1;
+ private static JTextField textField2;
+
+ public static void main(String[] args) throws Exception {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ frame = new JFrame("JTextField Test");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ JPanel container = (JPanel) frame.getContentPane();
+ container.setLayout(new GridLayout(2,1));
+
+ textField1 = new JTextField("\u0e01");
+ textField2 = new JTextField("\u0c01");
+
+ container.add(textField1);
+ container.add(textField2);
+ frame.setVisible(true);
+ frame.pack();
+ }
+ });
+ if( textField1.getHeight() < 10 || textField2.getHeight() < 10 )
+ throw new Exception("Wrong field height");
+ System.out.println("ok");
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ frame.dispose();
+ }
+ });
+ }
+}
--- a/jdk/test/javax/xml/jaxp/common/8032908/TestFunc.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/javax/xml/jaxp/common/8032908/TestFunc.java Thu Jun 04 18:49:37 2015 -0700
@@ -26,8 +26,9 @@
public class TestFunc {
public static String test(Node node) {
- String s = node.getTextContent();
- return s;
+ String textContent = node.getTextContent();
+ String nodeValue = node.getNodeValue();
+ return textContent + ":" + nodeValue;
}
}
--- a/jdk/test/javax/xml/jaxp/common/8032908/XSLT.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/javax/xml/jaxp/common/8032908/XSLT.java Thu Jun 04 18:49:37 2015 -0700
@@ -23,9 +23,10 @@
/**
* @test
- * @bug 8032908
+ * @bug 8032908 8081392
* @summary Test if Node.getTextContent() function correctly returns children
- * content
+ * content and also check that Node.getNodeValue() returns null value for
+ * Element nodes
* @compile TestFunc.java XSLT.java
* @run main/othervm XSLT
*/
@@ -40,7 +41,7 @@
static final String XMLTOTRANSFORM = "/in.xml";
static final String XSLTRANSFORMER = "/test.xsl";
- static final String EXPECTEDRESULT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>ABCDEFG";
+ static final String EXPECTEDRESULT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>ABCDEFG:null";
public static void main(String[] args) throws TransformerException {
ByteArrayOutputStream resStream = new ByteArrayOutputStream();
--- a/jdk/test/lib/security/CheckBlacklistedCerts.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/lib/security/CheckBlacklistedCerts.java Thu Jun 04 18:49:37 2015 -0700
@@ -45,7 +45,7 @@
File file = new File(home, "lib/security/cacerts");
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream fis = new FileInputStream(file)) {
- ks.load(new FileInputStream(file), null);
+ ks.load(fis, null);
}
System.out.println("Check for cacerts: " + ks.size());
for (String alias: Collections.list(ks.aliases())) {
--- a/jdk/test/lib/testlibrary/AssertsTest.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/lib/testlibrary/AssertsTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -21,6 +21,8 @@
* questions.
*/
+import java.lang.SuppressWarnings;
+
import static jdk.testlibrary.Asserts.*;
/* @test
@@ -75,7 +77,7 @@
private static void testEquals() throws Exception {
expectPass(Assertion.EQ, 1, 1);
- expectPass(Assertion.EQ, (Comparable)null, (Comparable)null);
+ expectPass(Assertion.EQ, (Integer)null, (Integer)null);
Foo f1 = new Foo(1);
expectPass(Assertion.EQ, f1, f1);
@@ -112,13 +114,13 @@
Foo f2 = new Foo(1);
expectPass(Assertion.NE, f1, f2);
- expectFail(Assertion.NE, (Comparable)null, (Comparable)null);
+ expectFail(Assertion.NE, (Integer)null, (Integer)null);
expectFail(Assertion.NE, f1, f1);
expectFail(Assertion.NE, 1, 1);
}
private static void testNull() throws Exception {
- expectPass(Assertion.NULL, (Comparable)null);
+ expectPass(Assertion.NULL, (Integer)null);
expectFail(Assertion.NULL, 1);
}
@@ -126,7 +128,7 @@
private static void testNotNull() throws Exception {
expectPass(Assertion.NOTNULL, 1);
- expectFail(Assertion.NOTNULL, (Comparable)null);
+ expectFail(Assertion.NOTNULL, (Integer)null);
}
private static void testTrue() throws Exception {
@@ -169,13 +171,13 @@
}
}
-
-
+ @SuppressWarnings("unchecked")
private static <T extends Comparable<T>> void expectPass(Assertion assertion, T ... args)
throws Exception {
Assertion.run(assertion, args);
}
+ @SuppressWarnings("unchecked")
private static <T extends Comparable<T>> void expectFail(Assertion assertion, T ... args)
throws Exception {
try {
@@ -192,8 +194,9 @@
enum Assertion {
LT, LTE, EQ, GTE, GT, NE, NULL, NOTNULL, FALSE, TRUE;
+ @SuppressWarnings("unchecked")
public static <T extends Comparable<T>> void run(Assertion assertion, T ... args) {
- String msg = "Expected " + format(assertion, args) + " to pass";
+ String msg = "Expected " + format(assertion, (Object[])args) + " to pass";
switch (assertion) {
case LT:
assertLessThan(args[0], args[1], msg);
--- a/jdk/test/lib/testlibrary/OutputAnalyzerReportingTest.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/lib/testlibrary/OutputAnalyzerReportingTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -28,7 +28,6 @@
* @summary Test the OutputAnalyzer reporting functionality,
* such as printing additional diagnostic info
* (exit code, stdout, stderr, command line, etc.)
- * @library /testlibrary
* @modules java.management
* @build jdk.testlibrary.*
* @run main jdk.testlibrary.OutputAnalyzerReportingTest
--- a/jdk/test/lib/testlibrary/OutputAnalyzerTest.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/lib/testlibrary/OutputAnalyzerTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -25,7 +25,6 @@
/*
* @test
* @summary Test the OutputAnalyzer utility class
- * @library /testlibrary
* @modules java.management
* @build jdk.testlibrary.*
* @run main jdk.testlibrary.OutputAnalyzerTest
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/JarUtils.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/JarUtils.java Thu Jun 04 18:49:37 2015 -0700
@@ -72,9 +72,9 @@
// is in the updated list
List<String> updatedFiles = new ArrayList<>();
try (JarFile srcJarFile = new JarFile(src)) {
- Enumeration entries = srcJarFile.entries();
+ Enumeration<JarEntry> entries = srcJarFile.entries();
while (entries.hasMoreElements()) {
- JarEntry entry = (JarEntry) entries.nextElement();
+ JarEntry entry = entries.nextElement();
String name = entry.getName();
boolean found = false;
for (String file : files) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/OptimalCapacity.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.testlibrary;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+
+/**
+ * Utility functions to check that the static storages are pre-sized
+ * optimally.
+ */
+public final class OptimalCapacity {
+
+ private OptimalCapacity() {}
+
+ /**
+ * Checks adequacy of the initial capacity of a static field
+ * of type {@code ArrayList}.
+ *
+ * Having
+ * <pre>
+ * class XClass {
+ * static ArrayList theList = new ArrayList(N);
+ * }
+ * </pre>
+ *
+ * you should call from the test
+ *
+ * <pre>
+ * OptimalCapacity.assertProperlySized(XClass.class, "theList", N);
+ * </pre>
+ */
+ public static void ofArrayList(Class<?> clazz, String fieldName,
+ int initialCapacity)
+ {
+ try {
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ Object obj = field.get(null);
+ if (!ArrayList.class.equals(obj.getClass())) {
+ throw new RuntimeException("'" + field +
+ "' expected to be of type ArrayList");
+ }
+ ArrayList<?> list = (ArrayList<?>)obj;
+
+ // For ArrayList the optimal capacity is its final size
+ if (list.size() != initialCapacity) {
+ throw new RuntimeException("Size of '" + field +
+ "' is " + list.size() +
+ ", but expected to be " + initialCapacity);
+ }
+ if (internalArraySize(list) != initialCapacity) {
+ throw new RuntimeException("Capacity of '" + field +
+ "' is " + internalArraySize(list) +
+ ", but expected to be " + initialCapacity);
+ }
+ } catch (ReflectiveOperationException roe) {
+ throw new RuntimeException(roe);
+ }
+ }
+
+ /**
+ * Checks adequacy of the initial capacity of a static field
+ * of type {@code HashMap}.
+ *
+ * Having
+ * <pre>
+ * class XClass {
+ * static HashMap theMap = new HashMap(N);
+ * }
+ * </pre>
+ *
+ * you should call from the test
+ *
+ * <pre>
+ * OptimalCapacity.ofHashMap(XClass.class, "theMap", N);
+ * </pre>
+ */
+ public static void ofHashMap(Class<?> clazz, String fieldName,
+ int initialCapacity)
+ {
+ try {
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ Object obj = field.get(null);
+ if (!HashMap.class.equals(obj.getClass())) {
+ throw new RuntimeException(field +
+ " expected to be of type HashMap");
+ }
+ HashMap<?,?> map = (HashMap<?,?>)obj;
+
+ // Check that the map allocates only necessary amount of space
+ HashMap<Object, Object> tmp = new HashMap<>(map);
+ if (internalArraySize(map) != internalArraySize(tmp)) {
+ throw new RuntimeException("Final capacity of '" + field +
+ "' is " + internalArraySize(map) +
+ ", which exceeds necessary minimum " + internalArraySize(tmp));
+ }
+
+ // Check that map is initially properly sized
+ tmp = new HashMap<>(initialCapacity);
+ tmp.put(new Object(), new Object()); // trigger storage init
+ if (internalArraySize(map) != internalArraySize(tmp)) {
+ throw new RuntimeException("Requested capacity of '" + field +
+ "' was " + initialCapacity +
+ ", which resulted in final capacity " + internalArraySize(tmp) +
+ ", which differs from necessary minimum " + internalArraySize(map));
+ }
+
+ } catch (ReflectiveOperationException roe) {
+ throw new RuntimeException(roe);
+ }
+ }
+
+ /**
+ * Checks adequacy of the expected maximum size of a static field
+ * of type {@code IdentityHashMap}.
+ *
+ * Having
+ * <pre>
+ * class XClass {
+ * static IdentityHashMap theMap = new IdentityHashMap(M);
+ * }
+ * </pre>
+ *
+ * you should call from the test
+ *
+ * <pre>
+ * OptimalCapacity.ofIdentityHashMap(XClass.class, "theMap", M);
+ * </pre>
+ */
+ public static void ofIdentityHashMap(Class<?> clazz, String fieldName,
+ int expectedMaxSize)
+ {
+ try {
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ Object obj = field.get(null);
+ if (!IdentityHashMap.class.equals(obj.getClass())) {
+ throw new RuntimeException("'" + field +
+ "' expected to be of type IdentityHashMap");
+ }
+ IdentityHashMap<?,?> map = (IdentityHashMap<?,?>)obj;
+
+ // Check that size of map is what was expected
+ if (map.size() != expectedMaxSize) {
+ throw new RuntimeException("Size of '" + field +
+ "' is " + map.size() +
+ ", which differs from expected " + expectedMaxSize);
+ }
+
+ // Check that the map allocated only necessary amount of memory
+ IdentityHashMap<Object, Object> tmp = new IdentityHashMap<>(map);
+ if (internalArraySize(map) != internalArraySize(tmp)) {
+ throw new RuntimeException("Final capacity of '" + field +
+ "' is " + internalArraySize(map) +
+ ", which exceeds necessary minimum " + internalArraySize(tmp));
+ }
+
+ // Check that map was initially properly sized
+ tmp = new IdentityHashMap<>(expectedMaxSize);
+ tmp.put(new Object(), new Object()); // trigger storage init
+ if (internalArraySize(map) != internalArraySize(tmp)) {
+ throw new RuntimeException("Requested number of elements in '" + field +
+ "' was " + expectedMaxSize +
+ ", which resulted in final capacity " + internalArraySize(tmp) +
+ ", which differs from necessary minimum " + internalArraySize(map));
+ }
+ } catch (ReflectiveOperationException roe) {
+ throw new RuntimeException(roe);
+ }
+ }
+
+ /**
+ * Returns size of the internal storage.
+ */
+ private static int internalArraySize(Object container)
+ throws ReflectiveOperationException {
+ Field field;
+ if (ArrayList.class.equals(container.getClass())) {
+ field = ArrayList.class.getDeclaredField("elementData");
+ } else if (HashMap.class.equals(container.getClass())) {
+ field = HashMap.class.getDeclaredField("table");
+ } else if (IdentityHashMap.class.equals(container.getClass())) {
+ field = IdentityHashMap.class.getDeclaredField("table");
+ } else {
+ throw new RuntimeException("Unexpected class " +
+ container.getClass());
+ }
+ field.setAccessible(true);
+ return ((Object[])field.get(container)).length;
+ }
+}
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/OutputBuffer.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/OutputBuffer.java Thu Jun 04 18:49:37 2015 -0700
@@ -30,6 +30,8 @@
class OutputBuffer {
private static class OutputBufferException extends RuntimeException {
+ private static final long serialVersionUID = 8528687792643129571L;
+
public OutputBufferException(Throwable cause) {
super(cause);
}
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/ParentLastURLClassLoader.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/ParentLastURLClassLoader.java Thu Jun 04 18:49:37 2015 -0700
@@ -38,7 +38,7 @@
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
- Class c = findClass(name);
+ Class<?> c = findClass(name);
if (c != null) {
return c;
}
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java Thu Jun 04 18:49:37 2015 -0700
@@ -72,7 +72,7 @@
public static Process startProcess(String name,
ProcessBuilder processBuilder)
throws IOException {
- return startProcess(name, processBuilder, (Consumer)null);
+ return startProcess(name, processBuilder, (Consumer<String>)null);
}
/**
@@ -85,6 +85,7 @@
* @return Returns the initialized process
* @throws IOException
*/
+ @SuppressWarnings("overloads")
public static Process startProcess(String name,
ProcessBuilder processBuilder,
Consumer<String> consumer)
@@ -239,6 +240,7 @@
* @throws InterruptedException
* @throws TimeoutException
*/
+ @SuppressWarnings("overloads")
public static Process startProcess(String name,
ProcessBuilder processBuilder,
final Predicate<String> linePredicate)
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/StreamPumper.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/StreamPumper.java Thu Jun 04 18:49:37 2015 -0700
@@ -77,7 +77,7 @@
private final Set<LinePump> linePumps = new HashSet<>();
private final AtomicBoolean processing = new AtomicBoolean(false);
- private final FutureTask<Void> processingTask = new FutureTask(this, null);
+ private final FutureTask<Void> processingTask = new FutureTask<>(this, null);
public StreamPumper(InputStream in) {
this.in = in;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/invoke/anon/ConstantPoolPatch/OptimalMapSize.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8080535
+ * @summary Static storages should be initialized with optimal capacity
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.OptimalCapacity
+ * @run main OptimalMapSize
+ */
+
+import jdk.testlibrary.OptimalCapacity;
+
+public class OptimalMapSize {
+ public static void main(String[] args) throws Throwable {
+ OptimalCapacity.ofIdentityHashMap(
+ Class.forName("sun.invoke.anon.ConstantPoolPatch"),
+ "CONSTANT_VALUE_CLASS_TAG", 6);
+ }
+}
--- a/jdk/test/sun/net/www/protocol/https/ChunkedOutputStream.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/sun/net/www/protocol/https/ChunkedOutputStream.java Thu Jun 04 18:49:37 2015 -0700
@@ -25,7 +25,6 @@
* @test
* @bug 5026745
* @modules java.base/sun.net.www
- * @library ../../httpstest/
* @build TestHttpsServer HttpCallback
* @run main/othervm ChunkedOutputStream
*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/Addresses.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8031111
+ * @summary fix krb5 caddr
+ * @compile -XDignore.symbol.file Addresses.java
+ * @run main/othervm Addresses
+ */
+
+import sun.security.krb5.Config;
+
+import javax.security.auth.kerberos.KerberosTicket;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+
+public class Addresses {
+
+ public static void main(String[] args) throws Exception {
+
+ KDC.saveConfig(OneKDC.KRB5_CONF, new OneKDC(null),
+ "noaddresses = false",
+ "extra_addresses = 10.0.0.10, 10.0.0.11 10.0.0.12");
+ Config.refresh();
+
+ KerberosTicket ticket =
+ Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false)
+ .s().getPrivateCredentials(KerberosTicket.class)
+ .iterator().next();
+
+ InetAddress loopback = InetAddress.getLoopbackAddress();
+ InetAddress extra1 = InetAddress.getByName("10.0.0.10");
+ InetAddress extra2 = InetAddress.getByName("10.0.0.11");
+ InetAddress extra3 = InetAddress.getByName("10.0.0.12");
+
+ boolean loopbackFound = false;
+ boolean extra1Found = false;
+ boolean extra2Found = false;
+ boolean extra3Found = false;
+ boolean networkFound = false;
+
+ for (InetAddress ia: ticket.getClientAddresses()) {
+ System.out.println(ia);
+ if (ia.equals(loopback)) {
+ loopbackFound = true;
+ System.out.println(" loopback found");
+ } else if (ia.equals(extra1)) {
+ extra1Found = true;
+ System.out.println(" extra1 found");
+ } else if (ia.equals(extra2)) {
+ extra2Found = true;
+ System.out.println(" extra2 found");
+ } else if (ia.equals(extra3)) {
+ extra3Found = true;
+ System.out.println(" extra3 found");
+ } else if (ia instanceof Inet4Address) {
+ networkFound = true;
+ System.out.println(" another address (" + ia +
+ "), assumed real network");
+ }
+ }
+
+ if (!loopbackFound || !networkFound
+ || !extra1Found || !extra2Found || !extra3Found ) {
+ throw new Exception();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/Forwarded.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8031111
+ * @summary fix krb5 caddr
+ * @compile -XDignore.symbol.file Forwarded.java
+ * @run main/othervm Forwarded
+ */
+
+import sun.security.jgss.GSSUtil;
+import sun.security.krb5.internal.KDCOptions;
+import sun.security.krb5.internal.KDCReqBody;
+import sun.security.krb5.internal.TGSReq;
+
+public class Forwarded {
+
+ public static void main(String[] args) throws Exception {
+
+ new OneKDC(null).setOption(KDC.Option.CHECK_ADDRESSES, true);
+
+ Context c;
+ c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
+
+ c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
+ c.x().requestCredDeleg(true);
+
+ c.take(new byte[0]);
+ }
+}
--- a/jdk/test/sun/security/krb5/auto/KDC.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/sun/security/krb5/auto/KDC.java Thu Jun 04 18:49:37 2015 -0700
@@ -205,6 +205,10 @@
* Sensitive accounts can never be delegated.
*/
SENSITIVE_ACCOUNTS,
+ /**
+ * If true, will check if TGS-REQ contains a non-null addresses field.
+ */
+ CHECK_ADDRESSES,
};
static {
@@ -734,6 +738,11 @@
bFlags[Krb5.TKT_OPTS_FORWARDABLE] = true;
}
}
+ if (options.containsKey(Option.CHECK_ADDRESSES)
+ && body.kdcOptions.get(KDCOptions.FORWARDED)
+ && body.addresses == null) {
+ throw new KrbException(Krb5.KDC_ERR_BADOPTION);
+ }
if (body.kdcOptions.get(KDCOptions.FORWARDED) ||
etp.flags.get(Krb5.TKT_OPTS_FORWARDED)) {
bFlags[Krb5.TKT_OPTS_FORWARDED] = true;
@@ -800,10 +809,8 @@
new KerberosTime(new Date()),
body.from,
till, body.rtime,
- body.addresses != null // always set caddr
- ? body.addresses
- : new HostAddresses(
- new InetAddress[]{InetAddress.getLocalHost()}),
+ body.addresses != null ? body.addresses
+ : etp.caddr,
null);
EncryptionKey skey = keyForUser(service, e3, true);
if (skey == null) {
@@ -826,10 +833,7 @@
body.from,
till, body.rtime,
service,
- body.addresses != null // always set caddr
- ? body.addresses
- : new HostAddresses(
- new InetAddress[]{InetAddress.getLocalHost()})
+ body.addresses
);
EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(), KeyUsage.KU_ENC_TGS_REP_PART_SESSKEY);
TGSRep tgsRep = new TGSRep(null,
--- a/jdk/test/sun/security/krb5/auto/MSOID2.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/sun/security/krb5/auto/MSOID2.java Thu Jun 04 18:49:37 2015 -0700
@@ -68,7 +68,14 @@
nt[0x1d] = (byte)0x82; // change the 1st to MS OID
// Length bytes to be tweaked
for (int pos: new int[] {3, 0xf, 0x13, 0x15, 0x17}) {
- nt[pos] = (byte)(nt[pos] + 11);
+ int newLen = (nt[pos]&0xff) + 11;
+ // The length byte at nt[pos] might overflow. It's
+ // unlikely for nt[pos-1] to overflow, which means the size
+ // of token is bigger than 65535.
+ if (newLen >= 256) {
+ nt[pos-1] = (byte)(nt[pos-1] + 1);
+ }
+ nt[pos] = (byte)newLen;
}
t = nt;
new sun.misc.HexDumpEncoder().encodeBuffer(t, System.out);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/SSLwithPerms.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8038089
+ * @summary TLS optional support for Kerberos cipher suites needs to be re-examined
+ * @library ../../../../java/security/testlibrary/
+ * @run main/othervm SSLwithPerms
+ */
+import java.io.*;
+import javax.net.ssl.*;
+import javax.security.auth.AuthPermission;
+import javax.security.auth.kerberos.ServicePermission;
+import java.net.SocketPermission;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.Principal;
+import java.security.Security;
+import java.security.SecurityPermission;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.PropertyPermission;
+
+import sun.security.jgss.GSSUtil;
+
+public class SSLwithPerms {
+
+ static String KRB5_CONF = "krb5.conf";
+ static String JAAS_CONF = "jaas.conf";
+ static String REALM = "REALM";
+ static String KTAB = "ktab";
+ static String HOST = "host." + REALM.toLowerCase(Locale.US);
+ static String SERVER = "host/" + HOST;
+ static String USER = "user";
+ static char[] PASS = "password".toCharArray();
+
+ public static void main(String[] args) throws Exception {
+
+ Security.setProperty("jdk.tls.disabledAlgorithms", "");
+ if (args.length == 0) {
+ KDC kdc = KDC.create(REALM, HOST, 0, true);
+
+ kdc.addPrincipal(USER, PASS);
+ kdc.addPrincipalRandKey("krbtgt/" + REALM);
+ kdc.addPrincipalRandKey(SERVER);
+ KDC.saveConfig(KRB5_CONF, kdc);
+ kdc.writeKtab(KTAB);
+
+ File f = new File(JAAS_CONF);
+ FileOutputStream fos = new FileOutputStream(f);
+ fos.write((
+ "ssl {\n" +
+ " com.sun.security.auth.module.Krb5LoginModule required\n" +
+ " principal=\"" + SERVER + "\"\n" +
+ " useKeyTab=true\n" +
+ " keyTab=" + KTAB + "\n" +
+ " isInitiator=false\n" +
+ " storeKey=true;\n};\n"
+ ).getBytes());
+ fos.close();
+
+ Proc pc = Proc.create("SSLwithPerms")
+ .args("client")
+ .inheritIO()
+ .prop("java.security.manager", "")
+ .prop("java.security.krb5.conf", KRB5_CONF)
+ .prop("sun.net.spi.nameservice.provider.1", "ns,mock")
+ .prop("javax.net.ssl", "handshake")
+ .prop("sun.security.krb5.debug", "true")
+ .perm(new SecurityPermission("setProperty.jdk.tls.disabledAlgorithms"))
+ .perm(new PropertyPermission("sun.security.krb5.principal", "read"))
+ .perm(new FilePermission("port", "read"))
+ .perm(new FilePermission(KTAB, "read"))
+ .perm(new RuntimePermission("accessClassInPackage.sun.net.spi.nameservice"))
+ .perm(new AuthPermission("modifyPrincipals"))
+ .perm(new AuthPermission("modifyPrivateCredentials"))
+ .perm(new AuthPermission("doAs"))
+ .perm(new SocketPermission("127.0.0.1", "connect"))
+ .perm(new ServicePermission("host/host.realm@REALM", "initiate"))
+ .start();
+
+ Proc ps = Proc.create("SSLwithPerms")
+ .args("server")
+ .inheritIO()
+ .prop("java.security.manager", "")
+ .prop("java.security.krb5.conf", KRB5_CONF)
+ .prop("java.security.auth.login.config", JAAS_CONF)
+ .prop("javax.net.ssl", "handshake")
+ .prop("sun.security.krb5.debug", "true")
+ .perm(new SecurityPermission("setProperty.jdk.tls.disabledAlgorithms"))
+ .perm(new AuthPermission("createLoginContext.ssl"))
+ .perm(new AuthPermission("doAs"))
+ .perm(new FilePermission("port", "write"))
+ .perm(new SocketPermission("127.0.0.1", "accept"))
+ .perm(new ServicePermission("host/host.realm@REALM", "accept"))
+ .start();
+
+ if (pc.waitFor() != 0) {
+ throw new Exception();
+ }
+ if (ps.waitFor() != 0) {
+ throw new Exception();
+ }
+ } else if (args[0].equals("client")) {
+ Context c;
+ c = Context.fromUserPass(USER, PASS, false);
+ c.doAs(new JsseClientAction(), null);
+ } else if (args[0].equals("server")) {
+ final Context s = Context.fromJAAS("ssl");
+ s.doAs(new JsseServerAction(), null);
+ }
+ }
+
+ private static class JsseClientAction implements Action {
+ public byte[] run(Context s, byte[] input) throws Exception {
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ while (!Files.exists(Paths.get("port"))) {
+ Thread.sleep(100);
+ }
+ int port = ByteBuffer.allocate(4)
+ .put(Files.readAllBytes(Paths.get("port"))).getInt(0);
+ System.out.println("Connecting " + SERVER + ":" + port);
+ SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(HOST, port);
+
+ // Enable only a KRB5 cipher suite.
+ String enabledSuites[] = {"TLS_KRB5_WITH_RC4_128_SHA"};
+ sslSocket.setEnabledCipherSuites(enabledSuites);
+
+ SSLParameters params = sslSocket.getSSLParameters();
+ params.setServerNames(Collections.singletonList(new SNIHostName(HOST)));
+ sslSocket.setSSLParameters(params);
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(
+ sslSocket.getInputStream()));
+ BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
+ sslSocket.getOutputStream()));
+
+ String outStr = "Hello There!\n";
+ out.write(outStr);
+ out.flush();
+ System.out.print("Sending " + outStr);
+
+ String inStr = in.readLine();
+ System.out.println("Received " + inStr);
+
+ String cipherSuiteChosen = sslSocket.getSession().getCipherSuite();
+ System.out.println("Cipher suite in use: " + cipherSuiteChosen);
+ Principal self = sslSocket.getSession().getLocalPrincipal();
+ System.out.println("I am: " + self.toString());
+ Principal peer = sslSocket.getSession().getPeerPrincipal();
+ System.out.println("Server is: " + peer.toString());
+
+ sslSocket.close();
+ return null;
+ }
+ }
+
+ private static class JsseServerAction implements Action {
+ public byte[] run(Context s, byte[] input) throws Exception {
+ SSLServerSocketFactory sslssf =
+ (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+ SSLServerSocket sslServerSocket =
+ (SSLServerSocket) sslssf.createServerSocket(0); // any port
+ int port = sslServerSocket.getLocalPort();
+ System.out.println("Listening on " + port);
+
+ String enabledSuites[] = {"TLS_KRB5_WITH_RC4_128_SHA"};
+ sslServerSocket.setEnabledCipherSuites(enabledSuites);
+
+ Files.write(Paths.get("port"), ByteBuffer.allocate(4).putInt(port).array());
+ System.out.println("Waiting for incoming connection...");
+
+ SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
+
+ System.out.println("Got connection from client "
+ + sslSocket.getInetAddress());
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(
+ sslSocket.getInputStream()));
+ BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
+ sslSocket.getOutputStream()));
+
+ String inStr = in.readLine();
+ System.out.println("Received " + inStr);
+
+ String outStr = inStr + " " + new Date().toString() + "\n";
+ out.write(outStr);
+ System.out.println("Sending " + outStr);
+ out.flush();
+
+ String cipherSuiteChosen =
+ sslSocket.getSession().getCipherSuite();
+ System.out.println("Cipher suite in use: " + cipherSuiteChosen);
+ Principal self = sslSocket.getSession().getLocalPrincipal();
+ System.out.println("I am: " + self.toString());
+ Principal peer = sslSocket.getSession().getPeerPrincipal();
+ System.out.println("Client is: " + peer.toString());
+
+ sslSocket.close();
+ return null;
+ }
+ }
+}
--- a/jdk/test/sun/security/ssl/AppInputStream/ReadHandshake.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/sun/security/ssl/AppInputStream/ReadHandshake.java Thu Jun 04 18:49:37 2015 -0700
@@ -21,19 +21,22 @@
* questions.
*/
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
/*
* @test
* @bug 4514971
* @summary Verify applications do not read handshake data after failure
* @run main/othervm ReadHandshake
- *
- * SunJSSE does not support dynamic system properties, no way to re-use
- * system properties in samevm/agentvm mode.
*/
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
+import java.security.Security;
public class ReadHandshake {
@@ -219,6 +222,10 @@
volatile Exception clientException = null;
public static void main(String[] args) throws Exception {
+ // reset security properties to make sure that the algorithms
+ // and keys used in this test are not disabled.
+ Security.setProperty("jdk.tls.disabledAlgorithms", "");
+ Security.setProperty("jdk.certpath.disabledAlgorithms", "");
if (debug)
System.setProperty("javax.net.debug", "all");
--- a/jdk/test/sun/security/ssl/ClientHandshaker/LengthCheckTest.java Thu Jun 04 09:31:49 2015 -0700
+++ b/jdk/test/sun/security/ssl/ClientHandshaker/LengthCheckTest.java Thu Jun 04 18:49:37 2015 -0700
@@ -233,7 +233,7 @@
// sent back to the server.
if (gotException == false ||
!isTlsMessage(cTOs, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
- TLS_ALERT_INTERNAL_ERROR)) {
+ TLS_ALERT_UNEXPECTED_MSG)) {
throw new SSLException(
"Client failed to throw Alert:fatal:internal_error");
}
@@ -285,7 +285,7 @@
// sent back to the client.
if (gotException == false ||
!isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
- TLS_ALERT_INTERNAL_ERROR)) {
+ TLS_ALERT_UNEXPECTED_MSG)) {
throw new SSLException(
"Server failed to throw Alert:fatal:internal_error");
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/ExtensionType/OptimalListSize.java Thu Jun 04 18:49:37 2015 -0700
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8080535
+ * @summary Expected size of Character.UnicodeBlock.map is not optimal
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.OptimalCapacity
+ * @run main OptimalListSize
+ */
+
+import jdk.testlibrary.OptimalCapacity;
+
+public class OptimalListSize {
+ public static void main(String[] args) throws Throwable {
+ OptimalCapacity.ofArrayList(
+ Class.forName("sun.security.ssl.ExtensionType"),
+ "knownExtensions", 13);
+ }
+}