blob: 8ecee5ed16bd75f3ae0cd4f971b61562068dc6f3 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2001-2003 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.jndi.ldap.sasl;
27
28import javax.security.sasl.Sasl;
29import javax.security.sasl.SaslClient;
30import javax.security.sasl.SaslException;
31import java.io.IOException;
32import java.io.EOFException;
33import java.io.InputStream;
34
35/**
36 * This class is used by clients of Java SASL that need to create an input stream
37 * that uses SaslClient's unwrap() method to decode the SASL buffers
38 * sent by the SASL server.
39 *
40 * Extend from InputStream instead of FilterInputStream because
41 * we need to override less methods in InputStream. That is, the
42 * behavior of the default implementations in InputStream matches
43 * more closely with the behavior we want in SaslInputStream.
44 *
45 * @author Rosanna Lee
46 */
47public class SaslInputStream extends InputStream {
48 private static final boolean debug = false;
49
50 private byte[] saslBuffer; // buffer for storing raw bytes
51 private byte[] lenBuf = new byte[4]; // buffer for storing length
52
53 private byte[] buf = new byte[0]; // buffer for storing processed bytes
54 // Initialized to empty buffer
55 private int bufPos = 0; // read position in buf
56 private InputStream in; // underlying input stream
57 private SaslClient sc;
58 private int recvMaxBufSize = 65536;
59
60 SaslInputStream(SaslClient sc, InputStream in) throws SaslException {
61 super();
62 this.in = in;
63 this.sc = sc;
64
65 String str = (String) sc.getNegotiatedProperty(Sasl.MAX_BUFFER);
66 if (str != null) {
67 try {
68 recvMaxBufSize = Integer.parseInt(str);
69 } catch (NumberFormatException e) {
70 throw new SaslException(Sasl.MAX_BUFFER +
71 " property must be numeric string: " + str);
72 }
73 }
74 saslBuffer = new byte[recvMaxBufSize];
75 }
76
77 public int read() throws IOException {
78 byte[] inBuf = new byte[1];
79 int count = read(inBuf, 0, 1);
80 if (count > 0) {
81 return inBuf[0];
82 } else {
83 return -1;
84 }
85 }
86
87 public int read(byte[] inBuf, int start, int count) throws IOException {
88
89 if (bufPos >= buf.length) {
90 int actual = fill(); // read and unwrap next SASL buffer
91 while (actual == 0) { // ignore zero length content
92 actual = fill();
93 }
94 if (actual == -1) {
95 return -1; // EOF
96 }
97 }
98
99 int avail = buf.length - bufPos;
100 if (count > avail) {
101 // Requesting more that we have stored
102 // Return all that we have; next invocation of read() will
103 // trigger fill()
104 System.arraycopy(buf, bufPos, inBuf, start, avail);
105 bufPos = buf.length;
106 return avail;
107 } else {
108 // Requesting less than we have stored
109 // Return all that was requested
110 System.arraycopy(buf, bufPos, inBuf, start, count);
111 bufPos += count;
112 return count;
113 }
114 }
115
116 /**
117 * Fills the buf with more data by reading a SASL buffer, unwrapping it,
118 * and leaving the bytes in buf for read() to return.
119 * @return The number of unwrapped bytes available
120 */
121 private int fill() throws IOException {
122 // Read in length of buffer
123 int actual = readFully(lenBuf, 4);
124 if (actual != 4) {
125 return -1;
126 }
127 int len = networkByteOrderToInt(lenBuf, 0, 4);
128
129 if (len > recvMaxBufSize) {
130 throw new IOException(
131 len + "exceeds the negotiated receive buffer size limit:" +
132 recvMaxBufSize);
133 }
134
135 if (debug) {
136 System.err.println("reading " + len + " bytes from network");
137 }
138
139 // Read SASL buffer
140 actual = readFully(saslBuffer, len);
141 if (actual != len) {
142 throw new EOFException("Expecting to read " + len +
143 " bytes but got " + actual + " bytes before EOF");
144 }
145
146 // Unwrap
147 buf = sc.unwrap(saslBuffer, 0, len);
148
149 bufPos = 0;
150
151 return buf.length;
152 }
153
154 /**
155 * Read requested number of bytes before returning.
156 * @return The number of bytes actually read; -1 if none read
157 */
158 private int readFully(byte[] inBuf, int total) throws IOException {
159 int count, pos = 0;
160
161 if (debug) {
162 System.err.println("readFully " + total + " from " + in);
163 }
164
165 while (total > 0) {
166 count = in.read(inBuf, pos, total);
167
168 if (debug) {
169 System.err.println("readFully read " + count);
170 }
171
172 if (count == -1 ) {
173 return (pos == 0? -1 : pos);
174 }
175 pos += count;
176 total -= count;
177 }
178 return pos;
179 }
180
181 public int available() throws IOException {
182 return buf.length - bufPos;
183 }
184
185 public void close() throws IOException {
186 SaslException save = null;
187 try {
188 sc.dispose(); // Dispose of SaslClient's state
189 } catch (SaslException e) {
190 // Save exception for throwing after closing 'in'
191 save = e;
192 }
193
194 in.close(); // Close underlying input stream
195
196 if (save != null) {
197 throw save;
198 }
199 }
200
201 /**
202 * Returns the integer represented by 4 bytes in network byte order.
203 */
204 // Copied from com.sun.security.sasl.util.SaslImpl.
205 private static int networkByteOrderToInt(byte[] buf, int start, int count) {
206 if (count > 4) {
207 throw new IllegalArgumentException("Cannot handle more than 4 bytes");
208 }
209
210 int answer = 0;
211
212 for (int i = 0; i < count; i++) {
213 answer <<= 8;
214 answer |= ((int)buf[start+i] & 0xff);
215 }
216 return answer;
217 }
218}