1 /*
   2  * Copyright (c) 1996, 2012, 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 
  27 package sun.security.ssl;
  28 
  29 import java.io.OutputStream;
  30 import java.io.IOException;
  31 
  32 /*
  33  * Output stream for application data. This is the kind of stream
  34  * that's handed out via SSLSocket.getOutputStream(). It's all the application
  35  * ever sees.
  36  *
  37  * Once the initial handshake has completed, application data may be
  38  * interleaved with handshake data. That is handled internally and remains
  39  * transparent to the application.
  40  *
  41  * @author  David Brownell
  42  */
  43 class AppOutputStream extends OutputStream {
  44 
  45     private SSLSocketImpl c;
  46     OutputRecord r;
  47 
  48     // One element array used to implement the write(byte) method
  49     private final byte[] oneByte = new byte[1];
  50 
  51     AppOutputStream(SSLSocketImpl conn) {
  52         r = new OutputRecord(Record.ct_application_data);
  53         c = conn;
  54     }
  55 
  56     /**
  57      * Write the data out, NOW.
  58      */
  59     @Override
  60     synchronized public void write(byte b[], int off, int len)
  61             throws IOException {
  62         if (b == null) {
  63             throw new NullPointerException();
  64         } else if (off < 0 || len < 0 || len > b.length - off) {
  65             throw new IndexOutOfBoundsException();
  66         } else if (len == 0) {
  67             return;
  68         }
  69 
  70         // check if the Socket is invalid (error or closed)
  71         c.checkWrite();
  72 
  73         /*
  74          * By default, we counter chosen plaintext issues on CBC mode
  75          * ciphersuites in SSLv3/TLS1.0 by sending one byte of application
  76          * data in the first record of every payload, and the rest in
  77          * subsequent record(s). Note that the issues have been solved in
  78          * TLS 1.1 or later.
  79          *
  80          * It is not necessary to split the very first application record of
  81          * a freshly negotiated TLS session, as there is no previous
  82          * application data to guess.  To improve compatibility, we will not
  83          * split such records.
  84          *
  85          * This avoids issues in the outbound direction.  For a full fix,
  86          * the peer must have similar protections.
  87          */
  88         boolean isFirstRecordOfThePayload = true;
  89 
  90         // Always flush at the end of each application level record.
  91         // This lets application synchronize read and write streams
  92         // however they like; if we buffered here, they couldn't.
  93         try {
  94             do {
  95                 boolean holdRecord = false;
  96                 int howmuch;
  97                 if (isFirstRecordOfThePayload && c.needToSplitPayload()) {
  98                     howmuch = Math.min(0x01, r.availableDataBytes());
  99                      /*
 100                       * Nagle's algorithm (TCP_NODELAY) was coming into
 101                       * play here when writing short (split) packets.
 102                       * Signal to the OutputRecord code to internally
 103                       * buffer this small packet until the next outbound
 104                       * packet (of any type) is written.
 105                       */
 106                      if ((len != 1) && (howmuch == 1)) {
 107                          holdRecord = true;
 108                      }
 109                 } else {
 110                     howmuch = Math.min(len, r.availableDataBytes());
 111                 }
 112 
 113                 if (isFirstRecordOfThePayload && howmuch != 0) {
 114                     isFirstRecordOfThePayload = false;
 115                 }
 116 
 117                 // NOTE: *must* call c.writeRecord() even for howmuch == 0
 118                 if (howmuch > 0) {
 119                     r.write(b, off, howmuch);
 120                     off += howmuch;
 121                     len -= howmuch;
 122                 }
 123                 c.writeRecord(r, holdRecord);
 124                 c.checkWrite();
 125             } while (len > 0);
 126         } catch (Exception e) {
 127             // shutdown and rethrow (wrapped) exception as appropriate
 128             c.handleException(e);
 129         }
 130     }
 131 
 132     /**
 133      * Write one byte now.
 134      */
 135     @Override
 136     synchronized public void write(int i) throws IOException {
 137         oneByte[0] = (byte)i;
 138         write(oneByte, 0, 1);
 139     }
 140 
 141     /*
 142      * Socket close is already synchronized, no need to block here.
 143      */
 144     @Override
 145     public void close() throws IOException {
 146         c.close();
 147     }
 148 
 149     // inherit no-op flush()
 150 }