0N/A/*
2362N/A * Copyright (c) 2001, 2003, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.jndi.ldap.sasl;
0N/A
0N/Aimport javax.security.sasl.Sasl;
0N/Aimport javax.security.sasl.SaslClient;
0N/Aimport javax.security.sasl.SaslException;
0N/Aimport java.io.IOException;
0N/Aimport java.io.EOFException;
0N/Aimport java.io.InputStream;
0N/A
0N/A/**
0N/A * This class is used by clients of Java SASL that need to create an input stream
0N/A * that uses SaslClient's unwrap() method to decode the SASL buffers
0N/A * sent by the SASL server.
0N/A *
0N/A * Extend from InputStream instead of FilterInputStream because
0N/A * we need to override less methods in InputStream. That is, the
0N/A * behavior of the default implementations in InputStream matches
0N/A * more closely with the behavior we want in SaslInputStream.
0N/A *
0N/A * @author Rosanna Lee
0N/A */
0N/Apublic class SaslInputStream extends InputStream {
0N/A private static final boolean debug = false;
0N/A
0N/A private byte[] saslBuffer; // buffer for storing raw bytes
0N/A private byte[] lenBuf = new byte[4]; // buffer for storing length
0N/A
0N/A private byte[] buf = new byte[0]; // buffer for storing processed bytes
0N/A // Initialized to empty buffer
0N/A private int bufPos = 0; // read position in buf
0N/A private InputStream in; // underlying input stream
0N/A private SaslClient sc;
0N/A private int recvMaxBufSize = 65536;
0N/A
0N/A SaslInputStream(SaslClient sc, InputStream in) throws SaslException {
0N/A super();
0N/A this.in = in;
0N/A this.sc = sc;
0N/A
0N/A String str = (String) sc.getNegotiatedProperty(Sasl.MAX_BUFFER);
0N/A if (str != null) {
0N/A try {
0N/A recvMaxBufSize = Integer.parseInt(str);
0N/A } catch (NumberFormatException e) {
0N/A throw new SaslException(Sasl.MAX_BUFFER +
0N/A " property must be numeric string: " + str);
0N/A }
0N/A }
0N/A saslBuffer = new byte[recvMaxBufSize];
0N/A }
0N/A
0N/A public int read() throws IOException {
0N/A byte[] inBuf = new byte[1];
0N/A int count = read(inBuf, 0, 1);
0N/A if (count > 0) {
0N/A return inBuf[0];
0N/A } else {
0N/A return -1;
0N/A }
0N/A }
0N/A
0N/A public int read(byte[] inBuf, int start, int count) throws IOException {
0N/A
0N/A if (bufPos >= buf.length) {
0N/A int actual = fill(); // read and unwrap next SASL buffer
0N/A while (actual == 0) { // ignore zero length content
0N/A actual = fill();
0N/A }
0N/A if (actual == -1) {
0N/A return -1; // EOF
0N/A }
0N/A }
0N/A
0N/A int avail = buf.length - bufPos;
0N/A if (count > avail) {
0N/A // Requesting more that we have stored
0N/A // Return all that we have; next invocation of read() will
0N/A // trigger fill()
0N/A System.arraycopy(buf, bufPos, inBuf, start, avail);
0N/A bufPos = buf.length;
0N/A return avail;
0N/A } else {
0N/A // Requesting less than we have stored
0N/A // Return all that was requested
0N/A System.arraycopy(buf, bufPos, inBuf, start, count);
0N/A bufPos += count;
0N/A return count;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Fills the buf with more data by reading a SASL buffer, unwrapping it,
0N/A * and leaving the bytes in buf for read() to return.
0N/A * @return The number of unwrapped bytes available
0N/A */
0N/A private int fill() throws IOException {
0N/A // Read in length of buffer
0N/A int actual = readFully(lenBuf, 4);
0N/A if (actual != 4) {
0N/A return -1;
0N/A }
0N/A int len = networkByteOrderToInt(lenBuf, 0, 4);
0N/A
0N/A if (len > recvMaxBufSize) {
0N/A throw new IOException(
0N/A len + "exceeds the negotiated receive buffer size limit:" +
0N/A recvMaxBufSize);
0N/A }
0N/A
0N/A if (debug) {
0N/A System.err.println("reading " + len + " bytes from network");
0N/A }
0N/A
0N/A // Read SASL buffer
0N/A actual = readFully(saslBuffer, len);
0N/A if (actual != len) {
0N/A throw new EOFException("Expecting to read " + len +
0N/A " bytes but got " + actual + " bytes before EOF");
0N/A }
0N/A
0N/A // Unwrap
0N/A buf = sc.unwrap(saslBuffer, 0, len);
0N/A
0N/A bufPos = 0;
0N/A
0N/A return buf.length;
0N/A }
0N/A
0N/A /**
0N/A * Read requested number of bytes before returning.
0N/A * @return The number of bytes actually read; -1 if none read
0N/A */
0N/A private int readFully(byte[] inBuf, int total) throws IOException {
0N/A int count, pos = 0;
0N/A
0N/A if (debug) {
0N/A System.err.println("readFully " + total + " from " + in);
0N/A }
0N/A
0N/A while (total > 0) {
0N/A count = in.read(inBuf, pos, total);
0N/A
0N/A if (debug) {
0N/A System.err.println("readFully read " + count);
0N/A }
0N/A
0N/A if (count == -1 ) {
0N/A return (pos == 0? -1 : pos);
0N/A }
0N/A pos += count;
0N/A total -= count;
0N/A }
0N/A return pos;
0N/A }
0N/A
0N/A public int available() throws IOException {
0N/A return buf.length - bufPos;
0N/A }
0N/A
0N/A public void close() throws IOException {
0N/A SaslException save = null;
0N/A try {
0N/A sc.dispose(); // Dispose of SaslClient's state
0N/A } catch (SaslException e) {
0N/A // Save exception for throwing after closing 'in'
0N/A save = e;
0N/A }
0N/A
0N/A in.close(); // Close underlying input stream
0N/A
0N/A if (save != null) {
0N/A throw save;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the integer represented by 4 bytes in network byte order.
0N/A */
0N/A // Copied from com.sun.security.sasl.util.SaslImpl.
0N/A private static int networkByteOrderToInt(byte[] buf, int start, int count) {
0N/A if (count > 4) {
0N/A throw new IllegalArgumentException("Cannot handle more than 4 bytes");
0N/A }
0N/A
0N/A int answer = 0;
0N/A
0N/A for (int i = 0; i < count; i++) {
0N/A answer <<= 8;
0N/A answer |= ((int)buf[start+i] & 0xff);
0N/A }
0N/A return answer;
0N/A }
0N/A}