![JAR search and dependency download from the Maven repository](/logo.png)
gnu.crypto.sasl.SaslInputStream Maven / Gradle / Ivy
package gnu.crypto.sasl;
// ----------------------------------------------------------------------------
// $Id: SaslInputStream.java,v 1.2 2003/05/30 12:58:45 raif Exp $
//
// Copyright (C) 2003 Free Software Foundation, Inc.
//
// This file is part of GNU Crypto.
//
// GNU Crypto is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// GNU Crypto is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING. If not, write to the
//
// Free Software Foundation Inc.,
// 59 Temple Place - Suite 330,
// Boston, MA 02111-1307
// USA
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library. Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give
// you permission to link this library with independent modules to
// produce an executable, regardless of the license terms of these
// independent modules, and to copy and distribute the resulting
// executable under terms of your choice, provided that you also meet,
// for each linked independent module, the terms and conditions of the
// license of that module. An independent module is a module which is
// not derived from or based on this library. If you modify this
// library, you may extend this exception to your version of the
// library, but you are not obligated to do so. If you do not wish to
// do so, delete this exception statement from your version.
// ----------------------------------------------------------------------------
import gnu.crypto.util.Util;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.IOException;
import java.io.PrintWriter;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslServer;
/**
* An input stream that uses either a {@link SaslClient} or a {@link SaslServer}
* to process the data through these entities' security layer filter(s).
*
* @version $Revision: 1.2 $
*/
public class SaslInputStream extends InputStream {
// Debugging methods and variables
// -------------------------------------------------------------------------
private static final String NAME = "SaslOutputStream";
private static final String ERROR = "ERROR";
private static final String WARN = " WARN";
// private static final String INFO = " INFO";
private static final String TRACE = "DEBUG";
private static final boolean DEBUG = true;
private static final int debuglevel = 3;
private static final PrintWriter err = new PrintWriter(System.out, true);
private static void debug(String level, Object obj) {
err.println("["+level+"] "+NAME+": "+String.valueOf(obj));
}
// Constants and variables
// -------------------------------------------------------------------------
private SaslClient client;
private SaslServer server;
private int maxRawSendSize;
private InputStream source;
private byte[] internalBuf;
// Constructor(s)
// -------------------------------------------------------------------------
public SaslInputStream(SaslClient client, InputStream source) throws IOException {
super();
this.client = client;
maxRawSendSize = Integer.parseInt(
(String) client.getNegotiatedProperty(Sasl.RAW_SEND_SIZE));
server = null;
this.source = source;
}
public SaslInputStream(SaslServer server, InputStream source) throws IOException {
super();
this.server = server;
maxRawSendSize = Integer.parseInt(
(String) server.getNegotiatedProperty(Sasl.RAW_SEND_SIZE));
client = null;
this.source = source;
}
// Class methods
// -------------------------------------------------------------------------
// Instance methods
// -------------------------------------------------------------------------
// Overloaded java.io.InputStream methods ----------------------------------
public int available() throws IOException {
return (internalBuf == null) ? 0 : internalBuf.length;
}
public void close() throws IOException {
source.close();
}
/**
* Reads the next byte of data from the input stream. The value byte is
* returned as an int
in the range 0
to
* 255
. If no byte is available because the end of the stream
* has been reached, the value -1
is returned. This method
* blocks until input data is available, the end of the stream is detected,
* or an exception is thrown.
*
* From a SASL mechanism provider's perspective, if a security layer has
* been negotiated, the underlying source is expected to contain SASL
* buffers, as defined in RFC 2222. Four octets in network byte order in the
* front of each buffer identify the length of the buffer. The provider is
* responsible for performing any integrity checking or other processing on
* the buffer before returning the data as a stream of octets. For example,
* the protocol driver's request for a single octet from the stream might;
* i.e. an invocation of this method, may result in an entire SASL buffer
* being read and processed before that single octet can be returned.
*
* @return the next byte of data, or -1
if the end of the stream
* is reached.
* @throws IOException if an I/O error occurs.
*/
public int read() throws IOException {
int result = -1;
if (internalBuf != null && internalBuf.length > 0) {
result = internalBuf[0] & 0xFF;
if (internalBuf.length == 1)
internalBuf = new byte[0];
else {
byte[] tmp = new byte[internalBuf.length - 1];
// System.arraycopy(internalBuf, 0, tmp, 0, tmp.length);
System.arraycopy(internalBuf, 1, tmp, 0, tmp.length);
internalBuf = tmp;
}
} else {
byte[] buf = new byte[1];
int check = read(buf);
result = (check > 0) ? (buf[0] & 0xFF) : -1;
}
return result;
}
/**
* Reads up to len
bytes of data from the underlying
* source input stream into an array of bytes. An attempt is made to
* read as many as len
bytes, but a smaller number may be read,
* possibly zero. The number of bytes actually read is returned as an
* integer.
*
* This method blocks until input data is available, end of file is
* detected, or an exception is thrown.
*
* If b
is null
, a {@link NullPointerException} is
* thrown.
*
* If off
is negative, or len
is negative, or
* off+len
is greater than the length of the array b
,
* then an {@link IndexOutOfBoundsException} is thrown.
*
* If len
is zero, then no bytes are read and 0
* is returned; otherwise, there is an attempt to read at least one byte. If
* no byte is available because the stream is at end of file, the value
* -1
is returned; otherwise, at least one byte is read and
* stored into b
.
*
* The first byte read is stored into element b[off]
, the
* next one into b[off+1]
, and so on. The number of bytes read
* is, at most, equal to len
. Let k
be the number
* of bytes actually read; these bytes will be stored in elements
* b[off]
through b[off+k-1]
, leaving elements
* b[off+k]
through b[off+len-1]
unaffected.
*
* In every case, elements b[0]
through b[off]
* and elements b[off+len]
through b[b.length-1]
* are unaffected.
*
* If the first byte cannot be read for any reason other than end of file,
* then an {@link IOException} is thrown. In particular, an {@link IOException}
* is thrown if the input stream has been closed.
*
* From the SASL mechanism provider's perspective, if a security layer has
* been negotiated, the underlying source is expected to contain SASL
* buffers, as defined in RFC 2222. Four octets in network byte order in the
* front of each buffer identify the length of the buffer. The provider is
* responsible for performing any integrity checking or other processing on
* the buffer before returning the data as a stream of octets. The protocol
* driver's request for a single octet from the stream might result in an
* entire SASL buffer being read and processed before that single octet can
* be returned.
*
* @param b the buffer into which the data is read.
* @param off the start offset in array b
at which the data is
* wricodeen.
* @param len the maximum number of bytes to read.
* @return the total number of bytes read into the buffer, or -1
* if there is no more data because the end of the stream has been reached.
* @throws IOException if an I/O error occurs.
*/
public int read(byte[] b, int off, int len) throws IOException {
if (DEBUG && debuglevel > 8) debug(TRACE, "==> read(b, "+String.valueOf(off)+", "+String.valueOf(len)+")");
if (b == null) {
throw new NullPointerException("b");
}
if ((off < 0)
|| (off > b.length)
|| (len < 0)
|| ((off + len) > b.length)
|| ((off + len) < 0)) {
throw new IndexOutOfBoundsException("off="+String.valueOf(off)
+", len="+String.valueOf(len)+", b.length="+String.valueOf(b.length));
}
if (len == 0) {
if (DEBUG && debuglevel > 8) debug(TRACE, "<== read() --> 0");
return 0;
}
if (DEBUG && debuglevel > 6) debug(TRACE, "Available: "+String.valueOf(available()));
int result = 0;
if (internalBuf == null || internalBuf.length < 1)
try {
internalBuf = readSaslBuffer();
if (internalBuf == null) {
if (DEBUG && debuglevel > 4) debug(WARN, "Underlying stream empty. Returning -1");
if (DEBUG && debuglevel > 8) debug(TRACE, "<== read() --> -1");
return -1;
}
} catch (InterruptedIOException x) {
if (DEBUG && debuglevel > 6) debug(TRACE, x);
if (DEBUG && debuglevel > 4) debug(WARN, "Reading thread was interrupted. Returning -1");
if (DEBUG && debuglevel > 8) debug(TRACE, "<== read() --> -1");
return -1;
}
if (len <= internalBuf.length) {
result = len;
System.arraycopy(internalBuf, 0, b, off, len);
if (len == internalBuf.length)
internalBuf = null;
else {
byte[] tmp = new byte[internalBuf.length - len];
System.arraycopy(internalBuf, len, tmp, 0, tmp.length);
internalBuf = tmp;
}
} else {
// first copy the available bytes to b
result = internalBuf.length;
System.arraycopy(internalBuf, 0, b, off, result);
internalBuf = null;
off += result;
len -= result;
int remaining; // count of bytes remaining in buffer after an iteration
int delta; // count of bytes moved to b after an iteration
int datalen;
byte[] data;
while (len > 0)
// we need to read SASL buffers, as long as there are at least
// 4 bytes available at the source
if (source.available() > 3) {
// process a buffer
data = readSaslBuffer();
if (data == null) {
if (DEBUG && debuglevel > 4) debug(WARN, "Underlying stream exhausted. Breaking...");
break;
}
datalen = data.length;
// copy [part of] the result to b
remaining = (datalen <= len) ? 0 : datalen - len;
delta = datalen - remaining;
System.arraycopy(data, 0, b, off, delta);
if (remaining > 0) {
internalBuf = new byte[remaining];
System.arraycopy(data, delta, internalBuf, 0, remaining);
}
// update off, result and len
off += delta;
result += delta;
len -= delta;
} else { // nothing much we can do except return what we have
if (DEBUG && debuglevel > 4) debug(WARN, "Not enough bytes in source to read a buffer. Breaking...");
break;
}
}
if (DEBUG && debuglevel > 6) debug(TRACE, "Remaining: "+(internalBuf == null ? 0 : internalBuf.length));
if (DEBUG && debuglevel > 8) debug(TRACE, "<== read() --> "+String.valueOf(result));
return result;
}
// other nstance methods ---------------------------------------------------
/**
* Reads a SASL buffer from the underlying source if at least 4 bytes are
* available.
*
* @return the byte[] of decoded buffer contents, or null if the underlying
* source was exhausted.
* @throws IOException if an I/O exception occurs during the operation.
*/
private byte[] readSaslBuffer() throws IOException {
if (DEBUG && debuglevel > 8) debug(TRACE, "==> readSaslBuffer()");
int realLength; // check if we read as many bytes as we're supposed to
byte[] result = new byte[4];
try {
realLength = source.read(result);
if (realLength == -1) {
if (DEBUG && debuglevel > 8) debug(TRACE, "<== readSaslBuffer() --> null");
return null;
}
} catch (IOException x) {
if (DEBUG && debuglevel > 0) debug(ERROR, x);
throw x;
}
if (realLength != 4) {
throw new IOException("Was expecting 4 but found "+String.valueOf(realLength));
}
int bufferLength = result[0] << 24 |
(result[1] & 0xFF) << 16 |
(result[2] & 0xFF) << 8 |
(result[3] & 0xFF);
if (DEBUG && debuglevel > 6) debug(TRACE, "SASL buffer size: "+bufferLength);
if (bufferLength > maxRawSendSize || bufferLength < 0) {
throw new SaslEncodingException("SASL buffer (security layer) too long");
}
result = new byte[bufferLength];
try {
realLength = source.read(result);
} catch (IOException x) {
if (DEBUG && debuglevel > 0) debug(ERROR, x);
throw x;
}
if (realLength != bufferLength)
throw new IOException("Was expecting "+String.valueOf(bufferLength)
+" but found "+String.valueOf(realLength));
if (DEBUG && debuglevel > 6) debug(TRACE, "Incoming buffer (before security) (hex): "+Util.dumpString(result));
if (DEBUG && debuglevel > 6) debug(TRACE, "Incoming buffer (before security) (str): \""+new String(result)+"\"");
if (client != null) {
result = client.unwrap(result, 0, realLength);
} else {
result = server.unwrap(result, 0, realLength);
}
if (DEBUG && debuglevel > 6) debug(TRACE, "Incoming buffer (after security) (hex): "+Util.dumpString(result));
if (DEBUG && debuglevel > 6) debug(TRACE, "Incoming buffer (after security) (str): \""+new String(result)+"\"");
if (DEBUG && debuglevel > 8) debug(TRACE, "<== readSaslBuffer()");
return result;
}
}