src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java
branchJDK-8145252-TLS13-branch
changeset 56542 56aaa6cb3693
parent 47216 71c04702a3d5
child 56660 66c803c3ce32
equal deleted inserted replaced
56541:92cbbfc996f3 56542:56aaa6cb3693
     1 /*
     1 /*
     2  * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 
    25 
    26 package sun.security.ssl;
    26 package sun.security.ssl;
    27 
    27 
    28 import java.io.*;
    28 import java.io.IOException;
    29 import java.nio.*;
    29 import java.nio.ByteBuffer;
    30 import java.util.*;
    30 import java.util.LinkedList;
    31 
       
    32 import javax.net.ssl.SSLException;
       
    33 import javax.net.ssl.SSLHandshakeException;
    31 import javax.net.ssl.SSLHandshakeException;
    34 import sun.security.util.HexDumpEncoder;
    32 
    35 import static sun.security.ssl.Ciphertext.RecordType;
    33 import sun.security.ssl.SSLCipher.SSLWriteCipher;
       
    34 import sun.security.ssl.KeyUpdate.KeyUpdateMessage;
    36 
    35 
    37 /**
    36 /**
    38  * {@code OutputRecord} implementation for {@code SSLEngine}.
    37  * {@code OutputRecord} implementation for {@code SSLEngine}.
    39  */
    38  */
    40 final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord {
    39 final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord {
    41 
    40 
    42     private HandshakeFragment fragmenter = null;
    41     private HandshakeFragment fragmenter = null;
    43     private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
       
    44     private boolean isTalkingToV2 = false;      // SSLv2Hello
    42     private boolean isTalkingToV2 = false;      // SSLv2Hello
    45     private ByteBuffer v2ClientHello = null;    // SSLv2Hello
    43     private ByteBuffer v2ClientHello = null;    // SSLv2Hello
    46 
    44 
    47     private boolean isCloseWaiting = false;
    45     private boolean isCloseWaiting = false;
    48 
    46 
    49     SSLEngineOutputRecord() {
    47     SSLEngineOutputRecord(HandshakeHash handshakeHash) {
    50         this.writeAuthenticator = MAC.TLS_NULL;
    48         super(handshakeHash, SSLWriteCipher.nullTlsWriteCipher());
    51 
    49 
    52         this.packetSize = SSLRecord.maxRecordSize;
    50         this.packetSize = SSLRecord.maxRecordSize;
    53         this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
    51         this.protocolVersion = ProtocolVersion.NONE;
    54     }
    52     }
    55 
    53 
    56     @Override
    54     @Override
    57     public synchronized void close() throws IOException {
    55     public synchronized void close() throws IOException {
    58         if (!isClosed) {
    56         if (!isClosed) {
    59             if (alertMemos != null && !alertMemos.isEmpty()) {
    57             if (fragmenter != null && fragmenter.hasAlert()) {
    60                 isCloseWaiting = true;
    58                 isCloseWaiting = true;
    61             } else {
    59             } else {
    62                 super.close();
    60                 super.close();
    63             }
    61             }
    64         }
    62         }
    65     }
    63     }
    66 
    64 
    67     @Override
    65     @Override
    68     void encodeAlert(byte level, byte description) throws IOException {
    66     void encodeAlert(byte level, byte description) throws IOException {
    69         RecordMemo memo = new RecordMemo();
    67         if (fragmenter == null) {
    70 
    68            fragmenter = new HandshakeFragment();
    71         memo.contentType = Record.ct_alert;
    69         }
    72         memo.majorVersion = protocolVersion.major;
    70 
    73         memo.minorVersion = protocolVersion.minor;
    71         fragmenter.queueUpAlert(level, description);
    74         memo.encodeCipher = writeCipher;
       
    75         memo.encodeAuthenticator = writeAuthenticator;
       
    76 
       
    77         memo.fragment = new byte[2];
       
    78         memo.fragment[0] = level;
       
    79         memo.fragment[1] = description;
       
    80 
       
    81         alertMemos.add(memo);
       
    82     }
    72     }
    83 
    73 
    84     @Override
    74     @Override
    85     void encodeHandshake(byte[] source,
    75     void encodeHandshake(byte[] source,
    86             int offset, int length) throws IOException {
    76             int offset, int length) throws IOException {
    91 
    81 
    92         if (firstMessage) {
    82         if (firstMessage) {
    93             firstMessage = false;
    83             firstMessage = false;
    94 
    84 
    95             if ((helloVersion == ProtocolVersion.SSL20Hello) &&
    85             if ((helloVersion == ProtocolVersion.SSL20Hello) &&
    96                 (source[offset] == HandshakeMessage.ht_client_hello) &&
    86                 (source[offset] == SSLHandshake.CLIENT_HELLO.id) &&
    97                                             //  5: recode header size
    87                                             //  5: recode header size
    98                 (source[offset + 4 + 2 + 32] == 0)) {
    88                 (source[offset + 4 + 2 + 32] == 0)) {
    99                                             // V3 session ID is empty
    89                                             // V3 session ID is empty
   100                                             //  4: handshake header size
    90                                             //  4: handshake header size
   101                                             //  2: client_version in ClientHello
    91                                             //  2: client_version in ClientHello
   104                 // Double space should be big enough for the converted message.
    94                 // Double space should be big enough for the converted message.
   105                 v2ClientHello = encodeV2ClientHello(
    95                 v2ClientHello = encodeV2ClientHello(
   106                         source, (offset + 4), (length - 4));
    96                         source, (offset + 4), (length - 4));
   107 
    97 
   108                 v2ClientHello.position(2);     // exclude the header
    98                 v2ClientHello.position(2);     // exclude the header
   109                 handshakeHash.update(v2ClientHello);
    99                 handshakeHash.deliver(v2ClientHello);
   110                 v2ClientHello.position(0);
   100                 v2ClientHello.position(0);
   111 
   101 
   112                 return;
   102                 return;
   113             }
   103             }
   114         }
   104         }
   115 
   105 
   116         byte handshakeType = source[offset];
   106         byte handshakeType = source[offset];
   117         if (handshakeType != HandshakeMessage.ht_hello_request) {
   107         if (handshakeHash.isHashable(handshakeType)) {
   118             handshakeHash.update(source, offset, length);
   108             handshakeHash.deliver(source, offset, length);
   119         }
   109         }
   120 
   110 
   121         fragmenter.queueUpFragment(source, offset, length);
   111         fragmenter.queueUpFragment(source, offset, length);
   122     }
   112     }
   123 
   113 
   133     void encodeV2NoCipher() throws IOException {
   123     void encodeV2NoCipher() throws IOException {
   134         isTalkingToV2 = true;
   124         isTalkingToV2 = true;
   135     }
   125     }
   136 
   126 
   137     @Override
   127     @Override
   138     Ciphertext encode(ByteBuffer[] sources, int offset, int length,
   128     Ciphertext encode(
       
   129         ByteBuffer[] srcs, int srcsOffset, int srcsLength,
       
   130         ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
       
   131         return encode(srcs, srcsOffset, srcsLength, dsts[0]);
       
   132     }
       
   133 
       
   134     private Ciphertext encode(ByteBuffer[] sources, int offset, int length,
   139             ByteBuffer destination) throws IOException {
   135             ByteBuffer destination) throws IOException {
   140 
   136 
   141         if (writeAuthenticator.seqNumOverflow()) {
   137         if (writeCipher.authenticator.seqNumOverflow()) {
   142             if (debug != null && Debug.isOn("ssl")) {
   138             if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
   143                 System.out.println(Thread.currentThread().getName() +
   139                 SSLLogger.fine(
   144                     ", sequence number extremely close to overflow " +
   140                     "sequence number extremely close to overflow " +
   145                     "(2^64-1 packets). Closing connection.");
   141                     "(2^64-1 packets). Closing connection.");
   146             }
   142             }
   147 
   143 
   148             throw new SSLHandshakeException("sequence number overflow");
   144             throw new SSLHandshakeException("sequence number overflow");
   149         }
   145         }
   150 
   146 
   151         int macLen = 0;
   147         // Don't process the incoming record until all of the
   152         if (writeAuthenticator instanceof MAC) {
   148         // buffered records get handled.
   153             macLen = ((MAC)writeAuthenticator).MAClen();
   149         Ciphertext ct = acquireCiphertext(destination);
       
   150         if (ct != null) {
       
   151             return ct;
       
   152         }
       
   153 
       
   154         if (sources == null || sources.length == 0) {
       
   155             return null;
       
   156         }
       
   157 
       
   158         int srcsRemains = 0;
       
   159         for (int i = offset; i < offset + length; i++) {
       
   160             srcsRemains += sources[i].remaining();
       
   161         }
       
   162 
       
   163         if (srcsRemains == 0) {
       
   164             return null;
   154         }
   165         }
   155 
   166 
   156         int dstLim = destination.limit();
   167         int dstLim = destination.limit();
   157         boolean isFirstRecordOfThePayload = true;
   168         boolean isFirstRecordOfThePayload = true;
   158         int packetLeftSize = Math.min(maxRecordSize, packetSize);
   169         int packetLeftSize = Math.min(maxRecordSize, packetSize);
   167                 isFirstRecordOfThePayload = false;
   178                 isFirstRecordOfThePayload = false;
   168             } else {
   179             } else {
   169                 needMorePayload = false;
   180                 needMorePayload = false;
   170 
   181 
   171                 if (packetLeftSize > 0) {
   182                 if (packetLeftSize > 0) {
   172                     fragLen = writeCipher.calculateFragmentSize(
   183                     fragLen =writeCipher.calculateFragmentSize(
   173                             packetLeftSize, macLen, headerSize);
   184                             packetLeftSize, headerSize);
   174 
   185 
   175                     fragLen = Math.min(fragLen, Record.maxDataSize);
   186                     fragLen = Math.min(fragLen, Record.maxDataSize);
   176                 } else {
   187                 } else {
   177                     fragLen = Record.maxDataSize;
   188                     fragLen = Record.maxDataSize;
   178                 }
   189                 }
   206             }
   217             }
   207 
   218 
   208             destination.limit(destination.position());
   219             destination.limit(destination.position());
   209             destination.position(dstContent);
   220             destination.position(dstContent);
   210 
   221 
   211             if ((debug != null) && Debug.isOn("record")) {
   222             if (SSLLogger.isOn && SSLLogger.isOn("record")) {
   212                 System.out.println(Thread.currentThread().getName() +
   223                 SSLLogger.fine(
   213                         ", WRITE: " + protocolVersion + " " +
   224                         "WRITE: " + protocolVersion + " " +
   214                         Record.contentName(Record.ct_application_data) +
   225                         ContentType.APPLICATION_DATA.name +
   215                         ", length = " + destination.remaining());
   226                         ", length = " + destination.remaining());
   216             }
   227             }
   217 
   228 
   218             // Encrypt the fragment and wrap up a record.
   229             // Encrypt the fragment and wrap up a record.
   219             recordSN = encrypt(writeAuthenticator, writeCipher,
   230             recordSN = encrypt(writeCipher,
   220                     Record.ct_application_data, destination,
   231                     ContentType.APPLICATION_DATA.id, destination,
   221                     dstPos, dstLim, headerSize,
   232                     dstPos, dstLim, headerSize,
   222                     protocolVersion, false);
   233                     protocolVersion);
   223 
   234 
   224             if ((debug != null) && Debug.isOn("packet")) {
   235             if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
   225                 ByteBuffer temporary = destination.duplicate();
   236                 ByteBuffer temporary = destination.duplicate();
   226                 temporary.limit(temporary.position());
   237                 temporary.limit(temporary.position());
   227                 temporary.position(dstPos);
   238                 temporary.position(dstPos);
   228                 Debug.printHex(
   239                 SSLLogger.fine("Raw write", temporary);
   229                         "[Raw write]: length = " + temporary.remaining(),
       
   230                         temporary);
       
   231             }
   240             }
   232 
   241 
   233             packetLeftSize -= destination.position() - dstPos;
   242             packetLeftSize -= destination.position() - dstPos;
   234 
   243 
   235             // remain the limit unchanged
   244             // remain the limit unchanged
   236             destination.limit(dstLim);
   245             destination.limit(dstLim);
   237 
   246 
   238             if (isFirstAppOutputRecord) {
   247             if (isFirstAppOutputRecord) {
   239                 isFirstAppOutputRecord = false;
   248                 isFirstAppOutputRecord = false;
   240             }
   249             }
   241         }
   250 
   242 
   251             if (writeCipher.atKeyLimit()) {
   243         return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
   252                 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
   244     }
   253                     SSLLogger.fine("KeyUpdate: triggered");
   245 
   254                 }
   246     @Override
   255 
   247     Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
   256                 PostHandshakeContext p = new PostHandshakeContext(tc);
       
   257                 KeyUpdate.handshakeProducer.produce(p,
       
   258                         new KeyUpdateMessage(p, KeyUpdateMessage.REQUSTED));
       
   259             }
       
   260         }
       
   261 
       
   262         return new Ciphertext(ContentType.APPLICATION_DATA.id,
       
   263                 SSLHandshake.NOT_APPLICABLE.id, recordSN);
       
   264     }
       
   265 
       
   266     private Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
   248         if (isTalkingToV2) {              // SSLv2Hello
   267         if (isTalkingToV2) {              // SSLv2Hello
   249             // We don't support SSLv2.  Send an SSLv2 error message
   268             // We don't support SSLv2.  Send an SSLv2 error message
   250             // so that the connection can be closed gracefully.
   269             // so that the connection can be closed gracefully.
   251             //
   270             //
   252             // Please don't change the limit of the destination buffer.
   271             // Please don't change the limit of the destination buffer.
   253             destination.put(SSLRecord.v2NoCipher);
   272             destination.put(SSLRecord.v2NoCipher);
   254             if (debug != null && Debug.isOn("packet")) {
   273             if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
   255                 Debug.printHex(
   274                 SSLLogger.fine("Raw write", SSLRecord.v2NoCipher);
   256                         "[Raw write]: length = " + SSLRecord.v2NoCipher.length,
       
   257                         SSLRecord.v2NoCipher);
       
   258             }
   275             }
   259 
   276 
   260             isTalkingToV2 = false;
   277             isTalkingToV2 = false;
   261 
   278 
   262             return new Ciphertext(RecordType.RECORD_ALERT, -1L);
   279             return new Ciphertext(ContentType.ALERT.id,
       
   280                     SSLHandshake.NOT_APPLICABLE.id, -1L);
   263         }
   281         }
   264 
   282 
   265         if (v2ClientHello != null) {
   283         if (v2ClientHello != null) {
   266             // deliver the SSLv2 format ClientHello message
   284             // deliver the SSLv2 format ClientHello message
   267             //
   285             //
   268             // Please don't change the limit of the destination buffer.
   286             // Please don't change the limit of the destination buffer.
   269             if (debug != null) {
   287             if (SSLLogger.isOn) {
   270                 if (Debug.isOn("record")) {
   288                 if (SSLLogger.isOn("record")) {
   271                      System.out.println(Thread.currentThread().getName() +
   289                      SSLLogger.fine(Thread.currentThread().getName() +
   272                             ", WRITE: SSLv2 ClientHello message" +
   290                             ", WRITE: SSLv2 ClientHello message" +
   273                             ", length = " + v2ClientHello.remaining());
   291                             ", length = " + v2ClientHello.remaining());
   274                 }
   292                 }
   275 
   293 
   276                 if (Debug.isOn("packet")) {
   294                 if (SSLLogger.isOn("packet")) {
   277                     Debug.printHex(
   295                     SSLLogger.fine("Raw write", v2ClientHello);
   278                         "[Raw write]: length = " + v2ClientHello.remaining(),
       
   279                         v2ClientHello);
       
   280                 }
   296                 }
   281             }
   297             }
   282 
   298 
   283             destination.put(v2ClientHello);
   299             destination.put(v2ClientHello);
   284             v2ClientHello = null;
   300             v2ClientHello = null;
   285 
   301 
   286             return new Ciphertext(RecordType.RECORD_CLIENT_HELLO, -1L);
   302             return new Ciphertext(ContentType.HANDSHAKE.id,
   287         }
   303                    SSLHandshake.CLIENT_HELLO.id, -1L);
   288 
       
   289         if (alertMemos != null && !alertMemos.isEmpty()) {
       
   290             RecordMemo memo = alertMemos.pop();
       
   291 
       
   292             int macLen = 0;
       
   293             if (memo.encodeAuthenticator instanceof MAC) {
       
   294                 macLen = ((MAC)memo.encodeAuthenticator).MAClen();
       
   295             }
       
   296 
       
   297             int dstPos = destination.position();
       
   298             int dstLim = destination.limit();
       
   299             int dstContent = dstPos + headerSize +
       
   300                                 writeCipher.getExplicitNonceSize();
       
   301             destination.position(dstContent);
       
   302 
       
   303             destination.put(memo.fragment);
       
   304 
       
   305             destination.limit(destination.position());
       
   306             destination.position(dstContent);
       
   307 
       
   308             if ((debug != null) && Debug.isOn("record")) {
       
   309                 System.out.println(Thread.currentThread().getName() +
       
   310                         ", WRITE: " + protocolVersion + " " +
       
   311                         Record.contentName(Record.ct_alert) +
       
   312                         ", length = " + destination.remaining());
       
   313             }
       
   314 
       
   315             // Encrypt the fragment and wrap up a record.
       
   316             long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
       
   317                     Record.ct_alert, destination, dstPos, dstLim, headerSize,
       
   318                     ProtocolVersion.valueOf(memo.majorVersion,
       
   319                             memo.minorVersion), false);
       
   320 
       
   321             if ((debug != null) && Debug.isOn("packet")) {
       
   322                 ByteBuffer temporary = destination.duplicate();
       
   323                 temporary.limit(temporary.position());
       
   324                 temporary.position(dstPos);
       
   325                 Debug.printHex(
       
   326                         "[Raw write]: length = " + temporary.remaining(),
       
   327                         temporary);
       
   328             }
       
   329 
       
   330             // remain the limit unchanged
       
   331             destination.limit(dstLim);
       
   332 
       
   333             if (isCloseWaiting && (memo.contentType == Record.ct_alert)) {
       
   334                 isCloseWaiting = true;
       
   335                 close();
       
   336             }
       
   337             return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
       
   338         }
   304         }
   339 
   305 
   340         if (fragmenter != null) {
   306         if (fragmenter != null) {
   341             return fragmenter.acquireCiphertext(destination);
   307             return fragmenter.acquireCiphertext(destination);
   342         }
   308         }
   345     }
   311     }
   346 
   312 
   347     @Override
   313     @Override
   348     boolean isEmpty() {
   314     boolean isEmpty() {
   349         return (!isTalkingToV2) && (v2ClientHello == null) &&
   315         return (!isTalkingToV2) && (v2ClientHello == null) &&
   350                 ((fragmenter == null) || fragmenter.isEmpty()) &&
   316                 ((fragmenter == null) || fragmenter.isEmpty());
   351                 ((alertMemos == null) || alertMemos.isEmpty());
       
   352     }
   317     }
   353 
   318 
   354     // buffered record fragment
   319     // buffered record fragment
   355     private static class RecordMemo {
   320     private static class RecordMemo {
   356         byte            contentType;
   321         byte            contentType;
   357         byte            majorVersion;
   322         byte            majorVersion;
   358         byte            minorVersion;
   323         byte            minorVersion;
   359         CipherBox       encodeCipher;
   324         SSLWriteCipher  encodeCipher;
   360         Authenticator   encodeAuthenticator;
       
   361 
   325 
   362         byte[]          fragment;
   326         byte[]          fragment;
   363     }
   327     }
   364 
   328 
   365     private static class HandshakeMemo extends RecordMemo {
   329     private static class HandshakeMemo extends RecordMemo {
   370     final class HandshakeFragment {
   334     final class HandshakeFragment {
   371         private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
   335         private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
   372 
   336 
   373         void queueUpFragment(byte[] source,
   337         void queueUpFragment(byte[] source,
   374                 int offset, int length) throws IOException {
   338                 int offset, int length) throws IOException {
   375 
       
   376             HandshakeMemo memo = new HandshakeMemo();
   339             HandshakeMemo memo = new HandshakeMemo();
   377 
   340 
   378             memo.contentType = Record.ct_handshake;
   341             memo.contentType = ContentType.HANDSHAKE.id;
   379             memo.majorVersion = protocolVersion.major;  // kick start version?
   342             memo.majorVersion = protocolVersion.major;  // kick start version?
   380             memo.minorVersion = protocolVersion.minor;
   343             memo.minorVersion = protocolVersion.minor;
   381             memo.encodeCipher = writeCipher;
   344             memo.encodeCipher = writeCipher;
   382             memo.encodeAuthenticator = writeAuthenticator;
       
   383 
   345 
   384             memo.handshakeType = source[offset];
   346             memo.handshakeType = source[offset];
   385             memo.acquireOffset = 0;
   347             memo.acquireOffset = 0;
   386             memo.fragment = new byte[length - 4];       // 4: header size
   348             memo.fragment = new byte[length - 4];       // 4: header size
   387                                                         //    1: HandshakeType
   349                                                         //    1: HandshakeType
   392         }
   354         }
   393 
   355 
   394         void queueUpChangeCipherSpec() {
   356         void queueUpChangeCipherSpec() {
   395             RecordMemo memo = new RecordMemo();
   357             RecordMemo memo = new RecordMemo();
   396 
   358 
   397             memo.contentType = Record.ct_change_cipher_spec;
   359             memo.contentType = ContentType.CHANGE_CIPHER_SPEC.id;
   398             memo.majorVersion = protocolVersion.major;
   360             memo.majorVersion = protocolVersion.major;
   399             memo.minorVersion = protocolVersion.minor;
   361             memo.minorVersion = protocolVersion.minor;
   400             memo.encodeCipher = writeCipher;
   362             memo.encodeCipher = writeCipher;
   401             memo.encodeAuthenticator = writeAuthenticator;
       
   402 
   363 
   403             memo.fragment = new byte[1];
   364             memo.fragment = new byte[1];
   404             memo.fragment[0] = 1;
   365             memo.fragment[0] = 1;
       
   366 
       
   367             handshakeMemos.add(memo);
       
   368         }
       
   369 
       
   370         void queueUpAlert(byte level, byte description) {
       
   371             RecordMemo memo = new RecordMemo();
       
   372 
       
   373             memo.contentType = ContentType.ALERT.id;
       
   374             memo.majorVersion = protocolVersion.major;
       
   375             memo.minorVersion = protocolVersion.minor;
       
   376             memo.encodeCipher = writeCipher;
       
   377 
       
   378             memo.fragment = new byte[2];
       
   379             memo.fragment[0] = level;
       
   380             memo.fragment[1] = description;
   405 
   381 
   406             handshakeMemos.add(memo);
   382             handshakeMemos.add(memo);
   407         }
   383         }
   408 
   384 
   409         Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
   385         Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
   411                 return null;
   387                 return null;
   412             }
   388             }
   413 
   389 
   414             RecordMemo memo = handshakeMemos.getFirst();
   390             RecordMemo memo = handshakeMemos.getFirst();
   415             HandshakeMemo hsMemo = null;
   391             HandshakeMemo hsMemo = null;
   416             if (memo.contentType == Record.ct_handshake) {
   392             if (memo.contentType == ContentType.HANDSHAKE.id) {
   417                 hsMemo = (HandshakeMemo)memo;
   393                 hsMemo = (HandshakeMemo)memo;
   418             }
       
   419 
       
   420             int macLen = 0;
       
   421             if (memo.encodeAuthenticator instanceof MAC) {
       
   422                 macLen = ((MAC)memo.encodeAuthenticator).MAClen();
       
   423             }
   394             }
   424 
   395 
   425             // ChangeCipherSpec message is pretty small.  Don't worry about
   396             // ChangeCipherSpec message is pretty small.  Don't worry about
   426             // the fragmentation of ChangeCipherSpec record.
   397             // the fragmentation of ChangeCipherSpec record.
   427             int fragLen;
   398             int fragLen;
   428             if (packetSize > 0) {
   399             if (packetSize > 0) {
   429                 fragLen = Math.min(maxRecordSize, packetSize);
   400                 fragLen = Math.min(maxRecordSize, packetSize);
   430                 fragLen = memo.encodeCipher.calculateFragmentSize(
   401                 fragLen = memo.encodeCipher.calculateFragmentSize(
   431                         fragLen, macLen, headerSize);
   402                         fragLen, headerSize);
   432             } else {
   403             } else {
   433                 fragLen = Record.maxDataSize;
   404                 fragLen = Record.maxDataSize;
   434             }
   405             }
   435 
   406 
   436             if (fragmentSize > 0) {
   407             if (fragmentSize > 0) {
   472                         // still have space for more records?
   443                         // still have space for more records?
   473                         if ((remainingFragLen > chipLen) &&
   444                         if ((remainingFragLen > chipLen) &&
   474                                  !handshakeMemos.isEmpty()) {
   445                                  !handshakeMemos.isEmpty()) {
   475 
   446 
   476                             // look for the next buffered record fragment
   447                             // look for the next buffered record fragment
   477                             RecordMemo reMemo = handshakeMemos.getFirst();
   448                             RecordMemo rm = handshakeMemos.getFirst();
   478                             if (reMemo.contentType == Record.ct_handshake) {
   449                             if (rm.contentType == ContentType.HANDSHAKE.id &&
   479                                 hsMemo = (HandshakeMemo)reMemo;
   450                                     rm.encodeCipher == hsMemo.encodeCipher) {
       
   451                                 hsMemo = (HandshakeMemo)rm;
   480                             } else {
   452                             } else {
   481                                 // not handshake message, break the loop
   453                                 // not of the flight, break the loop
   482                                 break;
   454                                 break;
   483                             }
   455                             }
   484                         }
   456                         }
   485                     }
   457                     }
   486 
   458 
   496             }
   468             }
   497 
   469 
   498             dstBuf.limit(dstBuf.position());
   470             dstBuf.limit(dstBuf.position());
   499             dstBuf.position(dstContent);
   471             dstBuf.position(dstContent);
   500 
   472 
   501             if ((debug != null) && Debug.isOn("record")) {
   473             if (SSLLogger.isOn && SSLLogger.isOn("record")) {
   502                 System.out.println(Thread.currentThread().getName() +
   474                 SSLLogger.fine(
   503                         ", WRITE: " + protocolVersion + " " +
   475                         "WRITE: " + protocolVersion + " " +
   504                         Record.contentName(memo.contentType) +
   476                         ContentType.nameOf(memo.contentType) +
   505                         ", length = " + dstBuf.remaining());
   477                         ", length = " + dstBuf.remaining());
   506             }
   478             }
   507 
   479 
   508             // Encrypt the fragment and wrap up a record.
   480             // Encrypt the fragment and wrap up a record.
   509             long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
   481             long recordSN = encrypt(
       
   482                     memo.encodeCipher,
   510                     memo.contentType, dstBuf,
   483                     memo.contentType, dstBuf,
   511                     dstPos, dstLim, headerSize,
   484                     dstPos, dstLim, headerSize,
   512                     ProtocolVersion.valueOf(memo.majorVersion,
   485                     ProtocolVersion.valueOf(memo.majorVersion,
   513                             memo.minorVersion), false);
   486                             memo.minorVersion));
   514 
   487 
   515             if ((debug != null) && Debug.isOn("packet")) {
   488             if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
   516                 ByteBuffer temporary = dstBuf.duplicate();
   489                 ByteBuffer temporary = dstBuf.duplicate();
   517                 temporary.limit(temporary.position());
   490                 temporary.limit(temporary.position());
   518                 temporary.position(dstPos);
   491                 temporary.position(dstPos);
   519                 Debug.printHex(
   492                 SSLLogger.fine("Raw write", temporary);
   520                         "[Raw write]: length = " + temporary.remaining(),
       
   521                         temporary);
       
   522             }
   493             }
   523 
   494 
   524             // remain the limit unchanged
   495             // remain the limit unchanged
   525             dstBuf.limit(dstLim);
   496             dstBuf.limit(dstLim);
   526 
   497 
   527             // Reset the fragmentation offset.
   498             // Reset the fragmentation offset.
   528             if (hsMemo != null) {
   499             if (hsMemo != null) {
   529                 return new Ciphertext(RecordType.valueOf(
   500                 return new Ciphertext(hsMemo.contentType,
   530                         hsMemo.contentType, hsMemo.handshakeType), recordSN);
   501                         hsMemo.handshakeType, recordSN);
   531             } else {
   502             } else {
   532                 return new Ciphertext(
   503                 if (isCloseWaiting &&
   533                         RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
   504                         memo.contentType == ContentType.ALERT.id) {
       
   505                     close();
       
   506                 }
       
   507 
       
   508                 return new Ciphertext(memo.contentType,
       
   509                         SSLHandshake.NOT_APPLICABLE.id, recordSN);
   534             }
   510             }
   535         }
   511         }
   536 
   512 
   537         boolean isEmpty() {
   513         boolean isEmpty() {
   538             return handshakeMemos.isEmpty();
   514             return handshakeMemos.isEmpty();
       
   515         }
       
   516 
       
   517         boolean hasAlert() {
       
   518             for (RecordMemo memo : handshakeMemos) {
       
   519                 if (memo.contentType ==  ContentType.ALERT.id) {
       
   520                     return true;
       
   521                 }
       
   522             }
       
   523 
       
   524             return false;
   539         }
   525         }
   540     }
   526     }
   541 
   527 
   542     /*
   528     /*
   543      * Need to split the payload except the following cases:
   529      * Need to split the payload except the following cases: