All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.vngx.jsch.Buffer Maven / Gradle / Ivy

Go to download

**vngx-jsch** (beta) is an updated version of the popular JSch SSH library written in pure Java. It has been updated to Java 6 with all the latest language features and improved code clarity.

The newest version!
/*
 * Copyright (c) 2010-2011 Michael Laudati, N1 Concepts LLC.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The names of the authors may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL N1
 * CONCEPTS LLC OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.vngx.jsch;

import java.util.Arrays;

/**
 * 

A simple byte Buffer implementation which wraps a byte array and provides * commonly used functionality for accessing and setting values according to the * SSH spec for packets. SSH packets contain a buffer of bytes which are sent * and received between the client and server.

* *

{@code Buffer} instances should have limited scope (mostly private or * package access) to ensure sensitive, secure information from an SSH session * is not externally exposed. Additionally, the {@link clear()} method zeros out * the entire internal buffer, clearing any secure data.

* *

RFC 4251 - The * Secure Shell (SSH) Protocol Architecture: Data Type Representations Used in * the SSH Protocols

*

RFC 4253 - The Secure Shell * (SSH) Transport Layer Protocol: Binary Packet Protocol

* *

Note: This class is not thread-safe and must be * externally synchronized.

* * @see org.vngx.jsch.Packet * * @author Michael Laudati */ public final class Buffer { // TODO Consider making default buffer size configurable /** Default size {@value} bytes when creating new {@code Buffer}s. */ final static int DEFAULT_SIZE = 1024 * 10 * 2; /** Array to serve as data buffer. */ byte[] buffer; /** Current index in the buffer when writing/putting data. */ int index; /** Current offset in the buffer when reading/getting data. */ private int _offset; /** * Creates a new instance of {@code Buffer} with the default size of * {@value #DEFAULT_SIZE} bytes. */ public Buffer() { this(DEFAULT_SIZE); } /** * Creates a new instance of {@code Buffer} with the specified {@code size} * in bytes. * * @param size of buffer in bytes */ public Buffer(final int size) { if( size < 0 ) { throw new IllegalArgumentException("Buffer must have a positive size: "+size); } else if( size > Packet.MAX_SIZE ) { throw new IllegalArgumentException("Buffer cannot exceed maximum packet size: "+size); } buffer = new byte[size]; } /** * Creates a new instance of {@code Buffer} wrapping the specified byte * {@code buffer}. * * @param buffer to wrap */ public Buffer(final byte[] buffer) { if( buffer == null ) { throw new IllegalArgumentException("Cannot wrap a null byte[]"); } else if( buffer.length > Packet.MAX_SIZE ) { throw new IllegalArgumentException("Buffer cannot exceed maximum packet size: "+buffer.length); } this.buffer = buffer; } /** * Puts the specified {@code byte} into the buffer at the current index. * * @param b byte to put in buffer * @return this instance */ public Buffer putByte(final byte b) { buffer[index++] = b; return this; } /** * Puts the entire contents of the specified {@code bytes} into the buffer * at the current index. * * @param bytes to put in buffer * @return this instance */ public Buffer putBytes(final byte[] bytes) { return putBytes(bytes, 0, bytes.length); } /** * Puts the contents of the specified {@code bytes} into the buffer at the * current index starting at {@code offset} position in the specified * {@code bytes} through the specified {@code length}. * * @param bytes to put in buffer * @param offset position in specified {@code bytes} * @param length of specified {@code bytes} to put * @return this instance */ public Buffer putBytes(final byte[] bytes, int offset, int length) { System.arraycopy(bytes, offset, buffer, index, length); index += length; return this; } /** * Puts the contents of the specified {@code Buffer} into the buffer at the * current index starting at {@code offset} position in the specified * {@code buffer} through the specified {@code length}. * * @param buffer to put in this buffer * @param offset position in specified {@code buffer} * @param length of specified {@code buffer} to put * @return this instance */ public Buffer putBytes(final Buffer buffer, int offset, int length) { return putBytes(buffer.getArray(), offset, length); } /** * Puts the specified {@code string} in the buffer at the current index. * Same as calling {@code putString(CharacterUtil.str2byte(string))}. * * @param string to put in buffer * @return this instance */ public Buffer putString(final String string) { return putString(Util.str2byte(string)); } /** * Puts the specified {@code string} data into this buffer at the current * index. * * @param string to put in buffer * @return this instance */ public Buffer putString(final byte[] string) { return putString(string, 0, string.length); } /** * Puts the specified {@code string} data into this buffer at the current * index starting at {@code offset} position in the specified {@code string} * through the specified {@code length}. * * @param string to put in buffer * @param offset position in specified {@code string} * @param length of {@code string} to put in buffer * @return this instance */ public Buffer putString(final byte[] string, int offset, int length) { putInt(length); return putBytes(string, offset, length); } /** * Puts the specified {@code int} into this buffer at the current index. * The value is stored as four bytes in the order of decreasing significance * (network byte order). For example: the value 699921578 (0x29b7f4aa) is * stored as 29 b7 f4 aa. (Assumes JVM standard size of 4 bytes for * {@code int} data type). * * @param value to put in buffer * @return this instance */ public Buffer putInt(final int value) { buffer[index++] = (byte) (value >>> 24); buffer[index++] = (byte) (value >>> 16); buffer[index++] = (byte) (value >>> 8); buffer[index++] = (byte) (value); return this; } /** * Puts the specified {@code long} into this buffer at the current index. * The value is stored as eight bytes in the order of decreasing * significance (network byte order). (Assumes JVM standard size of 8 bytes * for the {@code long} data type). * * @param value to put in buffer * @return this instance */ public Buffer putLong(final long value) { buffer[index++] = (byte) (value >>> 56); buffer[index++] = (byte) (value >>> 48); buffer[index++] = (byte) (value >>> 40); buffer[index++] = (byte) (value >>> 32); buffer[index++] = (byte) (value >>> 24); buffer[index++] = (byte) (value >>> 16); buffer[index++] = (byte) (value >>> 8); buffer[index++] = (byte) (value); return this; } /** * Places the specified boolean {@code b} into the buffer at the current * index. A boolean value of {@code true} is placed as a byte with value 1 * and {@code false} is placed as a byte with value 0. * * @param b boolean to put in buffer * @return this instance */ public Buffer putBoolean(final boolean b) { buffer[index++] = b ? (byte) 1 : (byte) 0; return this; } /** * Puts the multiple precision integer value {@code mpint} at the current * index. Multiple precision integers are represented in two's complement * format, stored as a string, 8 bits per byte, MSB first. Negative numbers * have the value 1 as the most significant bit of the first byte of the * data partition. If the most significant bit would be set for a positive * number, the number MUST be preceded by a zero byte. Unnecessary leading * bytes with the value 0 or 255 MUST NOT be included. The value zero MUST * be stored as a string with zero bytes of data. * * @param mpint to put in buffer * @return this instance */ public Buffer putMPInt(final byte[] mpint) { int length = mpint.length; if( length > 0 && (mpint[0] & 0x80) != 0 ) { // If first bit of mpint putInt(length+1); // is 1, then it's a negative number, it putByte((byte) 0); // needs to be encoded correctly } else { putInt(length); } return putBytes(mpint); } /** * Skips the current index forward by the specified amount {@code n}. * * @param n bytes to skip forward * @return this instance * @throws IndexOutOfBoundsException if n skip index past buffer length * @throws IllegalArgumentException if n is negative */ public Buffer skip(final int n) { if( (n + index) > buffer.length ) { throw new IndexOutOfBoundsException("Cannot skip index past buffer length"); } else if( n < 0 ) { throw new IllegalArgumentException("Cannot skip backwards"); } index += n; return this; } /** * Returns the size of the internal buffer. * * @return size of internal buffer in bytes */ public int size() { return buffer.length; } /** * Returns the current length of buffer (current index - offset). * * @return length of buffer */ public int getLength() { return index - _offset; } /** * Returns the current index position of buffer. The index maintains the * current write position where the next call to {@code putXxx} will insert * data. * * @return current index position */ public int getIndex() { return index; } /** * Returns the current offset position of buffer. The offset maintains the * current read position where the next call to {@code getXxx} will retrieve * data. * * @return current offset position */ public int getOffSet() { return _offset; } /** * Sets the current offset position of buffer. The offset maintains the * current read position where the next call to {@code getXxx} will retrieve * data. * * @param offset position to set * @return this instance * @throws IndexOutOfBoundsException if specified offset is greater than * buffer length or less than zero */ public Buffer setOffSet(final int offset) { if( offset > buffer.length ) { throw new IndexOutOfBoundsException("Offset cannot be greater than buffer length: "+offset); } else if( offset < 0 ) { throw new IndexOutOfBoundsException("Offset cannot be less than 0: "+offset); } _offset = offset; return this; } /** * Returns the internal backing {@code byte[]}. * * @return internal backing byte array */ public byte[] getArray() { return buffer; } /** * Returns the {@code long} value from the current offset position. * * @return {@code long} value from current offset */ public long getLong() { return ((getInt() & 0xFFFFFFFFL) << 32) | (getInt() & 0xFFFFFFFFL); } /** * Returns the {@code int} value from the current offset position. An * integer is made up of four bytes with the most significant bytes first. * * @return {@code int} value from current offset */ public int getInt() { return ((buffer[_offset++] << 24) & 0xFF000000) | ((buffer[_offset++] << 16) & 0x00FF0000) | ((buffer[_offset++] << 8) & 0x0000FF00) | ((buffer[_offset++] ) & 0x000000FF); } /** * Returns the unsigned {@code int} value from the current offset as a * {@code long}. An unsigned int is made up of four bytes with the most * significant bytes first. As an {@code int} in Java is always signed, the * value needs to be stored in a long so that large positive integer values * do not appear to be negative. * * @return unsigned {@code int} value as a long */ public long getUInt() { return (((long) getShort() << 16) & 0xFFFF0000L) | // First 16 bytes (( getShort() ) & 0x0000FFFFL); // second 16 bytes } /** * Returns the {@code short} value from the current offset position. A * short is made up of two bytes with the most significant bytes first. * * @return {@code short} value from current offset */ public int getShort() { return ((buffer[_offset++] << 8) & 0xFF00) | ((buffer[_offset++] ) & 0x00FF); } /** * Returns the {@code byte} value from the current offset position. The * value is returned as an {@code int} as bytes are unsigned, and may not * fit in a Java {@code byte} (i.e. values {@literal 127 < b < 256}). * * @return {@code byte} value from current offset as an {@code int} */ public int getByte() { return buffer[_offset++] & 0xFF; } /** * Returns the {@code boolean} value from the current offset position. A * boolean is stored as a {@code byte} value; {@code true} is any non-zero * byte value. * * @return {@code true} if byte at current offset does not equal zero */ public boolean getBoolean() { return getByte() != 0; } /** * Fills the specified {@code bytes} buffer with bytes from this buffer * starting from the current offset position through the length of * {@code bytes}. * * @param bytes array to fill with data * @return {@code bytes} passed to method */ public byte[] getBytes(final byte[] bytes) { return getBytes(bytes, 0, bytes.length); } /** * Fills the specified {@code bytes} buffer with bytes from this buffer * starting from the current offset position and filling {@ccode bytes} * starting at the specified {@code offset} through the specified * {@code length}. * * @param bytes array to fill with data * @param offset position in {@code bytes} * @param length to copy * @return {@code bytes} passed to method */ public byte[] getBytes(final byte[] bytes, int offset, int length) { System.arraycopy(buffer, _offset, bytes, offset, length); _offset += length; return bytes; } /** * Returns the multiple precision integer value from the current offset * position. * * @return multiple precision integer at the current offset */ public byte[] getMPInt() { final int mpIntLen = getInt(); // bigger than 0x7fffffff, will get OOME, throw exception if( mpIntLen < 0 || mpIntLen > 8 * 1024 ) { throw new IllegalStateException("MPInt exceeds maximum size: "+mpIntLen); } return getBytes(new byte[mpIntLen], 0, mpIntLen); } /** * Returns the multiple precision integer bits from the current offset. * * @return multiple precision integer bits */ public byte[] getMPIntBits() { int bits = getInt(); int bytes = (bits + 7) / 8; byte[] mpintBits = getBytes(new byte[bytes], 0, bytes); if( (mpintBits[0] & 0x80) != 0 ) { mpintBits = Util.join(new byte[1], mpintBits); // Set leading bit to 0, and shift rest down } return mpintBits; } /** * Returns the bytes of a {@code String} read from the current offset. * * @return bytes of a {@code String} from buffer */ public byte[] getString() { int strlen = getInt(); // bigger than 0x7fffffff, will get OOME, throw exception if( strlen < 0 || strlen > Packet.MAX_SIZE ) { throw new IllegalStateException("String length exceeds maximum: "+strlen); } return getBytes(new byte[strlen], 0, strlen); } /** * Reads the string from the buffer and returns the offset and length of the * internal buffer which holds the string, advancing the index to after the * string value. (Performance gain to create String from internal buffer * rather than create new byte[] and copying buffer content.) * *

Note: Use arrays with length 1 as parameters to allow for returning * multiple values (arrays passed by reference).

* * @param offset array to store the offset position of String at index 0 * @param length array to store the length of String at index 0 */ void getString(int[] offset, int[] length) { length[0] = getInt(); // Read length of String offset[0] = _offset; // Set offset of String as current offset _offset += length[0]; // Advance offset by length of String } /** * Resets the buffer to set the current index and offset positions to 0. * * @return this instance */ public Buffer reset() { index = _offset = 0; return this; } /** * Shifts the data in the internal buffer from its current offset to the * beginning of the array. * * @return this instance */ public Buffer shift() { if( _offset == 0 ) { return this; } System.arraycopy(buffer, _offset, buffer, 0, index - _offset); index -= _offset; _offset = 0; return this; } /** * Rewinds the current state by setting the offset to zero. * * @return this instance */ public Buffer rewind() { _offset = 0; return this; } /** * Returns the SSH command code. * * @return SSH command code */ public byte getCommand() { return buffer[5]; } /** * Ensures the capacity of the buffer to fit the specified {@code required} * amount by creating and copying data into a new internal buffer if the * current buffer does not have enough space starting from the current * index. If the requested size exceeds the maximum packet size, then an * {@code IllegalStateException} will be thrown. * * @param required amount in bytes to ensure * @return this instance * @throws IllegalStateException if total size is larger than max packet size */ public Buffer ensureCapacity(final int required) { if( (index + required) > buffer.length ) { if( (index + required) > Packet.MAX_SIZE ) { throw new IllegalStateException("Buffer cannot exceed max packet size: "+(index+required)); } buffer = Util.copyOf(buffer, index + required); } return this; } /** * Clears the entire internal buffer by setting all values to 0. * * @return this instance */ public Buffer clear() { reset(); Arrays.fill(buffer, (byte) 0); return this; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy