1 /*
   2  * Copyright (c) 2004, 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 package sun.security.ssl;
  27 
  28 import java.nio.*;
  29 
  30 /*
  31  * A multi-purpose class which handles all of the SSLEngine arguments.
  32  * It validates arguments, checks for RO conditions, does space
  33  * calculations, performs scatter/gather, etc.
  34  *
  35  * @author Brad R. Wetmore
  36  */
  37 class EngineArgs {
  38 
  39     /*
  40      * Keep track of the input parameters.
  41      */
  42     ByteBuffer netData;
  43     ByteBuffer [] appData;
  44 
  45     private int offset;         // offset/len for the appData array.
  46     private int len;
  47 
  48     /*
  49      * The initial pos/limit conditions.  This is useful because we can
  50      * quickly calculate the amount consumed/produced in successful
  51      * operations, or easily return the buffers to their pre-error
  52      * conditions.
  53      */
  54     private int netPos;
  55     private int netLim;
  56 
  57     private int [] appPoss;
  58     private int [] appLims;
  59 
  60     /*
  61      * Sum total of the space remaining in all of the appData buffers
  62      */
  63     private int appRemaining = 0;
  64 
  65     private boolean wrapMethod;
  66 
  67     /*
  68      * Called by the SSLEngine.wrap() method.
  69      */
  70     EngineArgs(ByteBuffer [] appData, int offset, int len,
  71             ByteBuffer netData) {
  72         this.wrapMethod = true;
  73         init(netData, appData, offset, len);
  74     }
  75 
  76     /*
  77      * Called by the SSLEngine.unwrap() method.
  78      */
  79     EngineArgs(ByteBuffer netData, ByteBuffer [] appData, int offset,
  80             int len) {
  81         this.wrapMethod = false;
  82         init(netData, appData, offset, len);
  83     }
  84 
  85     /*
  86      * The main initialization method for the arguments.  Most
  87      * of them are pretty obvious as to what they do.
  88      *
  89      * Since we're already iterating over appData array for validity
  90      * checking, we also keep track of how much remainging space is
  91      * available.  Info is used in both unwrap (to see if there is
  92      * enough space available in the destination), and in wrap (to
  93      * determine how much more we can copy into the outgoing data
  94      * buffer.
  95      */
  96     private void init(ByteBuffer netData, ByteBuffer [] appData,
  97             int offset, int len) {
  98 
  99         if ((netData == null) || (appData == null)) {
 100             throw new IllegalArgumentException("src/dst is null");
 101         }
 102 
 103         if ((offset < 0) || (len < 0) || (offset > appData.length - len)) {
 104             throw new IndexOutOfBoundsException();
 105         }
 106 
 107         if (wrapMethod && netData.isReadOnly()) {
 108             throw new ReadOnlyBufferException();
 109         }
 110 
 111         netPos = netData.position();
 112         netLim = netData.limit();
 113 
 114         appPoss = new int [appData.length];
 115         appLims = new int [appData.length];
 116 
 117         for (int i = offset; i < offset + len; i++) {
 118             if (appData[i] == null) {
 119                 throw new IllegalArgumentException(
 120                     "appData[" + i + "] == null");
 121             }
 122 
 123             /*
 124              * If we're unwrapping, then check to make sure our
 125              * destination bufffers are writable.
 126              */
 127             if (!wrapMethod && appData[i].isReadOnly()) {
 128                 throw new ReadOnlyBufferException();
 129             }
 130 
 131             appRemaining += appData[i].remaining();
 132 
 133             appPoss[i] = appData[i].position();
 134             appLims[i] = appData[i].limit();
 135         }
 136 
 137         /*
 138          * Ok, looks like we have a good set of args, let's
 139          * store the rest of this stuff.
 140          */
 141         this.netData = netData;
 142         this.appData = appData;
 143         this.offset = offset;
 144         this.len = len;
 145     }
 146 
 147     /*
 148      * Given spaceLeft bytes to transfer, gather up that much data
 149      * from the appData buffers (starting at offset in the array),
 150      * and transfer it into the netData buffer.
 151      *
 152      * The user has already ensured there is enough room.
 153      */
 154     void gather(int spaceLeft) {
 155         for (int i = offset; (i < (offset + len)) && (spaceLeft > 0); i++) {
 156             int amount = Math.min(appData[i].remaining(), spaceLeft);
 157             appData[i].limit(appData[i].position() + amount);
 158             netData.put(appData[i]);
 159             appRemaining -= amount;
 160             spaceLeft -= amount;
 161         }
 162     }
 163 
 164     /*
 165      * Using the supplied buffer, scatter the data into the appData buffers
 166      * (starting at offset in the array).
 167      *
 168      * The user has already ensured there is enough room.
 169      */
 170     void scatter(ByteBuffer readyData) {
 171         int amountLeft = readyData.remaining();
 172 
 173         for (int i = offset; (i < (offset + len)) && (amountLeft > 0);
 174                 i++) {
 175             int amount = Math.min(appData[i].remaining(), amountLeft);
 176             readyData.limit(readyData.position() + amount);
 177             appData[i].put(readyData);
 178             amountLeft -= amount;
 179         }
 180         assert(readyData.remaining() == 0);
 181     }
 182 
 183     int getAppRemaining() {
 184         return appRemaining;
 185     }
 186 
 187     /*
 188      * Calculate the bytesConsumed/byteProduced.  Aren't you glad
 189      * we saved this off earlier?
 190      */
 191     int deltaNet() {
 192         return (netData.position() - netPos);
 193     }
 194 
 195     /*
 196      * Calculate the bytesConsumed/byteProduced.  Aren't you glad
 197      * we saved this off earlier?
 198      */
 199     int deltaApp() {
 200         int sum = 0;    // Only calculating 2^14 here, don't need a long.
 201 
 202         for (int i = offset; i < offset + len; i++) {
 203             sum += appData[i].position() - appPoss[i];
 204         }
 205 
 206         return sum;
 207     }
 208 
 209     /*
 210      * In the case of Exception, we want to reset the positions
 211      * to appear as though no data has been consumed or produced.
 212      *
 213      * Currently, this method is only called as we are preparing to
 214      * fail out, and thus we don't need to actually recalculate
 215      * appRemaining.  If that assumption changes, that variable should
 216      * be updated here.
 217      */
 218     void resetPos() {
 219         netData.position(netPos);
 220         for (int i = offset; i < offset + len; i++) {
 221             // See comment above about recalculating appRemaining.
 222             appData[i].position(appPoss[i]);
 223         }
 224     }
 225 
 226     /*
 227      * We are doing lots of ByteBuffer manipulations, in which case
 228      * we need to make sure that the limits get set back correctly.
 229      * This is one of the last things to get done before returning to
 230      * the user.
 231      */
 232     void resetLim() {
 233         netData.limit(netLim);
 234         for (int i = offset; i < offset + len; i++) {
 235             appData[i].limit(appLims[i]);
 236         }
 237     }
 238 }