001: /*
002: * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.jndi.ldap;
027:
028: import java.io.UnsupportedEncodingException;
029:
030: /**
031: * A BER encoder.
032: *
033: * @author Jagane Sundar
034: * @author Scott Seligman
035: * @author Vincent Ryan
036: */
037: public final class BerEncoder extends Ber {
038:
039: private int curSeqIndex;
040: private int seqOffset[];
041: private static final int INITIAL_SEQUENCES = 16;
042: private static final int DEFAULT_BUFSIZE = 1024;
043:
044: // When buf is full, expand its size by the following factor.
045: private static final int BUF_GROWTH_FACTOR = 8;
046:
047: /**
048: * Creates a BER buffer for encoding.
049: */
050: public BerEncoder() {
051: this (DEFAULT_BUFSIZE);
052: }
053:
054: /**
055: * Creates a BER buffer of a specified size for encoding.
056: * Specify the initial bufsize. Buffer will be expanded as needed.
057: * @param bufsize The number of bytes for the buffer.
058: */
059: public BerEncoder(int bufsize) {
060: buf = new byte[bufsize];
061: this .bufsize = bufsize;
062: offset = 0;
063:
064: seqOffset = new int[INITIAL_SEQUENCES];
065: curSeqIndex = 0;
066: }
067:
068: /**
069: * Resets encoder to state when newly constructed. Zeros out
070: * internal data structures.
071: */
072: public void reset() {
073: while (offset > 0) {
074: buf[--offset] = 0;
075: }
076: while (curSeqIndex > 0) {
077: seqOffset[--curSeqIndex] = 0;
078: }
079: }
080:
081: // ------------------ Accessor methods ------------
082:
083: /**
084: * Gets the number of encoded bytes in this BER buffer.
085: */
086: public int getDataLen() {
087: return offset;
088: }
089:
090: /**
091: * Gets the buffer that contains the BER encoding. Throws an
092: * exception if unmatched beginSeq() and endSeq() pairs were
093: * encountered. Not entire buffer contains encoded bytes.
094: * Use getDataLen() to determine number of encoded bytes.
095: * Use getBuffer(true) to get rid of excess bytes in array.
096: * @throws IllegalStateException If buffer contains unbalanced sequence.
097: */
098: public byte[] getBuf() {
099: if (curSeqIndex != 0) {
100: throw new IllegalStateException(
101: "BER encode error: Unbalanced SEQUENCEs.");
102: }
103: return buf;
104: }
105:
106: /**
107: * Gets the buffer that contains the BER encoding, trimming unused bytes.
108: *
109: * @throws IllegalStateException If buffer contains unbalanced sequence.
110: */
111: public byte[] getTrimmedBuf() {
112: int len = getDataLen();
113: byte[] trimBuf = new byte[len];
114:
115: System.arraycopy(getBuf(), 0, trimBuf, 0, len);
116: return trimBuf;
117: }
118:
119: // -------------- encoding methods -------------
120:
121: /**
122: * Begin encoding a sequence with a tag.
123: */
124: public void beginSeq(int tag) {
125:
126: // Double the size of the SEQUENCE array if it overflows
127: if (curSeqIndex >= seqOffset.length) {
128: int[] seqOffsetTmp = new int[seqOffset.length * 2];
129:
130: for (int i = 0; i < seqOffset.length; i++) {
131: seqOffsetTmp[i] = seqOffset[i];
132: }
133: seqOffset = seqOffsetTmp;
134: }
135:
136: encodeByte(tag);
137: seqOffset[curSeqIndex] = offset;
138:
139: // Save space for sequence length.
140: // %%% Currently we save enough space for sequences up to 64k.
141: // For larger sequences we'll need to shift the data to the right
142: // in endSeq(). If we could instead pad the length field with
143: // zeros, it would be a big win.
144: ensureFreeBytes(3);
145: offset += 3;
146:
147: curSeqIndex++;
148: }
149:
150: /**
151: * Terminate a BER sequence.
152: */
153: public void endSeq() throws EncodeException {
154: curSeqIndex--;
155: if (curSeqIndex < 0) {
156: throw new IllegalStateException(
157: "BER encode error: Unbalanced SEQUENCEs.");
158: }
159:
160: int start = seqOffset[curSeqIndex] + 3; // index beyond length field
161: int len = offset - start;
162:
163: if (len <= 0x7f) {
164: shiftSeqData(start, len, -2);
165: buf[seqOffset[curSeqIndex]] = (byte) len;
166: } else if (len <= 0xff) {
167: shiftSeqData(start, len, -1);
168: buf[seqOffset[curSeqIndex]] = (byte) 0x81;
169: buf[seqOffset[curSeqIndex] + 1] = (byte) len;
170: } else if (len <= 0xffff) {
171: buf[seqOffset[curSeqIndex]] = (byte) 0x82;
172: buf[seqOffset[curSeqIndex] + 1] = (byte) (len >> 8);
173: buf[seqOffset[curSeqIndex] + 2] = (byte) len;
174: } else if (len <= 0xffffff) {
175: shiftSeqData(start, len, 1);
176: buf[seqOffset[curSeqIndex]] = (byte) 0x83;
177: buf[seqOffset[curSeqIndex] + 1] = (byte) (len >> 16);
178: buf[seqOffset[curSeqIndex] + 2] = (byte) (len >> 8);
179: buf[seqOffset[curSeqIndex] + 3] = (byte) len;
180: } else {
181: throw new EncodeException("SEQUENCE too long");
182: }
183: }
184:
185: /**
186: * Shifts contents of buf in the range [start,start+len) a specified amount.
187: * Positive shift value means shift to the right.
188: */
189: private void shiftSeqData(int start, int len, int shift) {
190: if (shift > 0) {
191: ensureFreeBytes(shift);
192: }
193: System.arraycopy(buf, start, buf, start + shift, len);
194: offset += shift;
195: }
196:
197: /**
198: * Encode a single byte.
199: */
200: public void encodeByte(int b) {
201: ensureFreeBytes(1);
202: buf[offset++] = (byte) b;
203: }
204:
205: /*
206: private void deleteByte() {
207: offset--;
208: }
209: */
210:
211: /*
212: * Encodes an int.
213: *<blockquote><pre>
214: * BER integer ::= 0x02 berlength byte {byte}*
215: *</pre></blockquote>
216: */
217: public void encodeInt(int i) {
218: encodeInt(i, 0x02);
219: }
220:
221: /**
222: * Encodes an int and a tag.
223: *<blockquote><pre>
224: * BER integer w tag ::= tag berlength byte {byte}*
225: *</pre></blockquote>
226: */
227: public void encodeInt(int i, int tag) {
228: int mask = 0xff800000;
229: int intsize = 4;
230:
231: while ((((i & mask) == 0) || ((i & mask) == mask))
232: && (intsize > 1)) {
233: intsize--;
234: i <<= 8;
235: }
236:
237: encodeInt(i, tag, intsize);
238: }
239:
240: //
241: // encodes an int using numbytes for the actual encoding.
242: //
243: private void encodeInt(int i, int tag, int intsize) {
244:
245: //
246: // integer ::= 0x02 asnlength byte {byte}*
247: //
248:
249: if (intsize > 4) {
250: throw new IllegalArgumentException(
251: "BER encode error: INTEGER too long.");
252: }
253:
254: ensureFreeBytes(2 + intsize);
255:
256: buf[offset++] = (byte) tag;
257: buf[offset++] = (byte) intsize;
258:
259: int mask = 0xff000000;
260:
261: while (intsize-- > 0) {
262: buf[offset++] = (byte) ((i & mask) >> 24);
263: i <<= 8;
264: }
265: }
266:
267: /**
268: * Encodes a boolean.
269: *<blockquote><pre>
270: * BER boolean ::= 0x01 0x01 {0xff|0x00}
271: *</pre></blockquote>
272: */
273: public void encodeBoolean(boolean b) {
274: encodeBoolean(b, ASN_BOOLEAN);
275: }
276:
277: /**
278: * Encodes a boolean and a tag
279: *<blockquote><pre>
280: * BER boolean w TAG ::= tag 0x01 {0xff|0x00}
281: *</pre></blockquote>
282: */
283: public void encodeBoolean(boolean b, int tag) {
284: ensureFreeBytes(3);
285:
286: buf[offset++] = (byte) tag;
287: buf[offset++] = 0x01;
288: buf[offset++] = b ? (byte) 0xff : (byte) 0x00;
289: }
290:
291: /**
292: * Encodes a string.
293: *<blockquote><pre>
294: * BER string ::= 0x04 strlen byte1 byte2...
295: *</pre></blockquote>
296: * The string is converted into bytes using UTF-8 or ISO-Latin-1.
297: */
298: public void encodeString(String str, boolean encodeUTF8)
299: throws EncodeException {
300: encodeString(str, ASN_OCTET_STR, encodeUTF8);
301: }
302:
303: /**
304: * Encodes a string and a tag.
305: *<blockquote><pre>
306: * BER string w TAG ::= tag strlen byte1 byte2...
307: *</pre></blockquote>
308: */
309: public void encodeString(String str, int tag, boolean encodeUTF8)
310: throws EncodeException {
311:
312: encodeByte(tag);
313:
314: int i = 0;
315: int count;
316: byte[] bytes = null;
317:
318: if (str == null) {
319: count = 0;
320: } else if (encodeUTF8) {
321: try {
322: bytes = str.getBytes("UTF8");
323: count = bytes.length;
324: } catch (UnsupportedEncodingException e) {
325: throw new EncodeException(
326: "UTF8 not available on platform");
327: }
328: } else {
329: try {
330: bytes = str.getBytes("8859_1");
331: count = bytes.length;
332: } catch (UnsupportedEncodingException e) {
333: throw new EncodeException(
334: "8859_1 not available on platform");
335: }
336: }
337:
338: encodeLength(count);
339:
340: ensureFreeBytes(count);
341: while (i < count) {
342: buf[offset++] = bytes[i++];
343: }
344: }
345:
346: /**
347: * Encodes a portion of an octet string and a tag.
348: */
349: public void encodeOctetString(byte tb[], int tag, int tboffset,
350: int length) throws EncodeException {
351:
352: encodeByte(tag);
353: encodeLength(length);
354:
355: if (length > 0) {
356: ensureFreeBytes(length);
357: System.arraycopy(tb, tboffset, buf, offset, length);
358: offset += length;
359: }
360: }
361:
362: /**
363: * Encodes an octet string and a tag.
364: */
365: public void encodeOctetString(byte tb[], int tag)
366: throws EncodeException {
367: encodeOctetString(tb, tag, 0, tb.length);
368: }
369:
370: private void encodeLength(int len) throws EncodeException {
371: ensureFreeBytes(4); // worst case
372:
373: if (len < 128) {
374: buf[offset++] = (byte) len;
375: } else if (len <= 0xff) {
376: buf[offset++] = (byte) 0x81;
377: buf[offset++] = (byte) len;
378: } else if (len <= 0xffff) {
379: buf[offset++] = (byte) 0x82;
380: buf[offset++] = (byte) (len >> 8);
381: buf[offset++] = (byte) (len & 0xff);
382: } else if (len <= 0xffffff) {
383: buf[offset++] = (byte) 0x83;
384: buf[offset++] = (byte) (len >> 16);
385: buf[offset++] = (byte) (len >> 8);
386: buf[offset++] = (byte) (len & 0xff);
387: } else {
388: throw new EncodeException("string too long");
389: }
390: }
391:
392: /**
393: * Encodes an array of strings.
394: */
395: public void encodeStringArray(String strs[], boolean encodeUTF8)
396: throws EncodeException {
397: if (strs == null)
398: return;
399: for (int i = 0; i < strs.length; i++) {
400: encodeString(strs[i], encodeUTF8);
401: }
402: }
403:
404: /*
405: private void encodeNull() {
406:
407: //
408: // NULL ::= 0x05 0x00
409: //
410: encodeByte(0x05);
411: encodeByte(0x00);
412: }
413: */
414:
415: /**
416: * Ensures that there are at least "len" unused bytes in "buf".
417: * When more space is needed "buf" is expanded by a factor of
418: * BUF_GROWTH_FACTOR, then "len" bytes are added if "buf" still
419: * isn't large enough.
420: */
421: private void ensureFreeBytes(int len) {
422: if (bufsize - offset < len) {
423: int newsize = bufsize * BUF_GROWTH_FACTOR;
424: if (newsize - offset < len) {
425: newsize += len;
426: }
427: byte newbuf[] = new byte[newsize];
428: // Only copy bytes in the range [0, offset)
429: System.arraycopy(buf, 0, newbuf, 0, offset);
430:
431: buf = newbuf;
432: bufsize = newsize;
433: }
434: }
435: }
|