Home | History | Annotate | Download | only in ssl
      1 /*
      2  * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      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
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package sun.security.ssl;
     27 
     28 import javax.net.ssl.*;
     29 import java.io.IOException;
     30 import java.nio.ByteBuffer;
     31 import java.util.LinkedList;
     32 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
     33 import sun.misc.HexDumpEncoder;
     34 
     35 /**
     36  * A class to help abstract away SSLEngine writing synchronization.
     37  */
     38 final class EngineWriter {
     39 
     40     /*
     41      * Outgoing handshake Data waiting for a ride is stored here.
     42      * Normal application data is written directly into the outbound
     43      * buffer, but handshake data can be written out at any time,
     44      * so we have buffer it somewhere.
     45      *
     46      * When wrap is called, we first check to see if there is
     47      * any data waiting, then if we're in a data transfer state,
     48      * we try to write app data.
     49      *
     50      * This will contain either ByteBuffers, or the marker
     51      * HandshakeStatus.FINISHED to signify that a handshake just completed.
     52      */
     53     private LinkedList<Object> outboundList;
     54 
     55     private boolean outboundClosed = false;
     56 
     57     /* Class and subclass dynamic debugging support */
     58     private static final Debug debug = Debug.getInstance("ssl");
     59 
     60     EngineWriter() {
     61         outboundList = new LinkedList<Object>();
     62     }
     63 
     64     /*
     65      * Upper levels assured us we had room for at least one packet of data.
     66      * As per the SSLEngine spec, we only return one SSL packets worth of
     67      * data.
     68      */
     69     private HandshakeStatus getOutboundData(ByteBuffer dstBB) {
     70 
     71         Object msg = outboundList.removeFirst();
     72         assert(msg instanceof ByteBuffer);
     73 
     74         ByteBuffer bbIn = (ByteBuffer) msg;
     75         assert(dstBB.remaining() >= bbIn.remaining());
     76 
     77         dstBB.put(bbIn);
     78 
     79         /*
     80          * If we have more data in the queue, it's either
     81          * a finished message, or an indication that we need
     82          * to call wrap again.
     83          */
     84         if (hasOutboundDataInternal()) {
     85             msg = outboundList.getFirst();
     86             if (msg == HandshakeStatus.FINISHED) {
     87                 outboundList.removeFirst();     // consume the message
     88                 return HandshakeStatus.FINISHED;
     89             } else {
     90                 return HandshakeStatus.NEED_WRAP;
     91             }
     92         } else {
     93             return null;
     94         }
     95     }
     96 
     97     /*
     98      * Properly orders the output of the data written to the wrap call.
     99      * This is only handshake data, application data goes through the
    100      * other writeRecord.
    101      */
    102     synchronized void writeRecord(EngineOutputRecord outputRecord,
    103             MAC writeMAC, CipherBox writeCipher) throws IOException {
    104 
    105         /*
    106          * Only output if we're still open.
    107          */
    108         if (outboundClosed) {
    109             throw new IOException("writer side was already closed.");
    110         }
    111 
    112         outputRecord.write(writeMAC, writeCipher);
    113 
    114         /*
    115          * Did our handshakers notify that we just sent the
    116          * Finished message?
    117          *
    118          * Add an "I'm finished" message to the queue.
    119          */
    120         if (outputRecord.isFinishedMsg()) {
    121             outboundList.addLast(HandshakeStatus.FINISHED);
    122         }
    123     }
    124 
    125     /*
    126      * Output the packet info.
    127      */
    128     private void dumpPacket(EngineArgs ea, boolean hsData) {
    129         try {
    130             HexDumpEncoder hd = new HexDumpEncoder();
    131 
    132             ByteBuffer bb = ea.netData.duplicate();
    133 
    134             int pos = bb.position();
    135             bb.position(pos - ea.deltaNet());
    136             bb.limit(pos);
    137 
    138             System.out.println("[Raw write" +
    139                 (hsData ? "" : " (bb)") + "]: length = " +
    140                 bb.remaining());
    141             hd.encodeBuffer(bb, System.out);
    142         } catch (IOException e) { }
    143     }
    144 
    145     /*
    146      * Properly orders the output of the data written to the wrap call.
    147      * Only app data goes through here, handshake data goes through
    148      * the other writeRecord.
    149      *
    150      * Shouldn't expect to have an IOException here.
    151      *
    152      * Return any determined status.
    153      */
    154     synchronized HandshakeStatus writeRecord(
    155             EngineOutputRecord outputRecord, EngineArgs ea, MAC writeMAC,
    156             CipherBox writeCipher) throws IOException {
    157 
    158         /*
    159          * If we have data ready to go, output this first before
    160          * trying to consume app data.
    161          */
    162         if (hasOutboundDataInternal()) {
    163             HandshakeStatus hss = getOutboundData(ea.netData);
    164 
    165             if (debug != null && Debug.isOn("packet")) {
    166                 /*
    167                  * We could have put the dump in
    168                  * OutputRecord.write(OutputStream), but let's actually
    169                  * output when it's actually output by the SSLEngine.
    170                  */
    171                 dumpPacket(ea, true);
    172             }
    173 
    174             return hss;
    175         }
    176 
    177         /*
    178          * If we are closed, no more app data can be output.
    179          * Only existing handshake data (above) can be obtained.
    180          */
    181         if (outboundClosed) {
    182             throw new IOException("The write side was already closed");
    183         }
    184 
    185         outputRecord.write(ea, writeMAC, writeCipher);
    186 
    187         if (debug != null && Debug.isOn("packet")) {
    188             dumpPacket(ea, false);
    189         }
    190 
    191         /*
    192          * No way new outbound handshake data got here if we're
    193          * locked properly.
    194          *
    195          * We don't have any status we can return.
    196          */
    197         return null;
    198     }
    199 
    200     /*
    201      * We already hold "this" lock, this is the callback from the
    202      * outputRecord.write() above.  We already know this
    203      * writer can accept more data (outboundClosed == false),
    204      * and the closure is sync'd.
    205      */
    206     void putOutboundData(ByteBuffer bytes) {
    207         outboundList.addLast(bytes);
    208     }
    209 
    210     /*
    211      * This is for the really rare case that someone is writing from
    212      * the *InputRecord* before we know what to do with it.
    213      */
    214     synchronized void putOutboundDataSync(ByteBuffer bytes)
    215             throws IOException {
    216 
    217         if (outboundClosed) {
    218             throw new IOException("Write side already closed");
    219         }
    220 
    221         outboundList.addLast(bytes);
    222     }
    223 
    224     /*
    225      * Non-synch'd version of this method, called by internals
    226      */
    227     private boolean hasOutboundDataInternal() {
    228         return (outboundList.size() != 0);
    229     }
    230 
    231     synchronized boolean hasOutboundData() {
    232         return hasOutboundDataInternal();
    233     }
    234 
    235     synchronized boolean isOutboundDone() {
    236         return outboundClosed && !hasOutboundDataInternal();
    237     }
    238 
    239     synchronized void closeOutbound() {
    240         outboundClosed = true;
    241     }
    242 
    243 }
    244