|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
package com.sun.jmx.snmp; |
|
|
|
|
|
|
|
|
|
/** |
|
* The <CODE>BerDecoder</CODE> class is used for decoding |
|
* BER-encoded data. |
|
* |
|
* A <CODE>BerDecoder</CODE> needs to be set up with the byte string containing |
|
* the encoding. It maintains a current position in the byte string. |
|
* |
|
* Methods allows to fetch integer, string, OID, etc., from the current |
|
* position. After a fetch the current position is moved forward. |
|
* |
|
* A fetch throws a <CODE>BerException</CODE> if the encoding is not of the |
|
* expected type. |
|
* |
|
* <p><b>This API is a Sun Microsystems internal API and is subject |
|
* to change without notice.</b></p> |
|
* |
|
* @since 1.5 |
|
*/ |
|
|
|
public class BerDecoder { |
|
|
|
/** |
|
* Constructs a new decoder and attaches it to the specified byte string. |
|
* |
|
* @param b The byte string containing the encoded data. |
|
*/ |
|
|
|
public BerDecoder(byte b[]) { |
|
bytes = b ; |
|
reset() ; |
|
} |
|
|
|
public void reset() { |
|
next = 0 ; |
|
stackTop = 0 ; |
|
} |
|
|
|
/** |
|
* Fetch an integer. |
|
* |
|
* @return The decoded integer. |
|
* |
|
* @exception BerException Current position does not point to an integer. |
|
*/ |
|
|
|
public int fetchInteger() throws BerException { |
|
return fetchInteger(IntegerTag) ; |
|
} |
|
|
|
|
|
/** |
|
* Fetch an integer with the specified tag. |
|
* |
|
* @param tag The expected tag. |
|
* |
|
* @return The decoded integer. |
|
* |
|
* @exception BerException Current position does not point to an integer |
|
* or the tag is not the expected one. |
|
*/ |
|
|
|
public int fetchInteger(int tag) throws BerException { |
|
int result = 0 ; |
|
final int backup = next ; |
|
try { |
|
if (fetchTag() != tag) { |
|
throw new BerException() ; |
|
} |
|
result = fetchIntegerValue() ; |
|
} |
|
catch(BerException e) { |
|
next = backup ; |
|
throw e ; |
|
} |
|
|
|
return result ; |
|
} |
|
|
|
|
|
|
|
/** |
|
* Fetch an integer and return a long value. |
|
* |
|
* @return The decoded integer. |
|
* |
|
* @exception BerException Current position does not point to an integer. |
|
*/ |
|
|
|
public long fetchIntegerAsLong() throws BerException { |
|
return fetchIntegerAsLong(IntegerTag) ; |
|
} |
|
|
|
|
|
/** |
|
* Fetch an integer with the specified tag and return a long value. |
|
* |
|
* @param tag The expected tag. |
|
* |
|
* @return The decoded integer. |
|
* |
|
* @exception BerException Current position does not point to an integer |
|
* or the tag is not the expected one. |
|
*/ |
|
|
|
public long fetchIntegerAsLong(int tag) throws BerException { |
|
long result = 0 ; |
|
final int backup = next ; |
|
try { |
|
if (fetchTag() != tag) { |
|
throw new BerException() ; |
|
} |
|
result = fetchIntegerValueAsLong() ; |
|
} |
|
catch(BerException e) { |
|
next = backup ; |
|
throw e ; |
|
} |
|
|
|
return result ; |
|
} |
|
|
|
|
|
|
|
/** |
|
* Fetch an octet string. |
|
* |
|
* @return The decoded string. |
|
* |
|
* @exception BerException Current position does not point to an octet string. |
|
*/ |
|
|
|
public byte[] fetchOctetString() throws BerException { |
|
return fetchOctetString(OctetStringTag) ; |
|
} |
|
|
|
|
|
/** |
|
* Fetch an octet string with a specified tag. |
|
* |
|
* @param tag The expected tag. |
|
* |
|
* @return The decoded string. |
|
* |
|
* @exception BerException Current position does not point to an octet string |
|
* or the tag is not the expected one. |
|
*/ |
|
|
|
public byte[] fetchOctetString(int tag) throws BerException { |
|
byte[] result = null ; |
|
final int backup = next ; |
|
try { |
|
if (fetchTag() != tag) { |
|
throw new BerException() ; |
|
} |
|
result = fetchStringValue() ; |
|
} |
|
catch(BerException e) { |
|
next = backup ; |
|
throw e ; |
|
} |
|
|
|
return result ; |
|
} |
|
|
|
|
|
/** |
|
* Fetch an object identifier. |
|
* |
|
* @return The decoded object identifier as an array of long. |
|
*/ |
|
|
|
public long[] fetchOid() throws BerException { |
|
return fetchOid(OidTag) ; |
|
} |
|
|
|
|
|
/** |
|
* Fetch an object identifier with a specified tag. |
|
* |
|
* @param tag The expected tag. |
|
* |
|
* @return The decoded object identifier as an array of long. |
|
* |
|
* @exception BerException Current position does not point to an oid |
|
* or the tag is not the expected one. |
|
*/ |
|
|
|
public long[] fetchOid(int tag) throws BerException { |
|
long[] result = null ; |
|
final int backup = next ; |
|
try { |
|
if (fetchTag() != tag) { |
|
throw new BerException() ; |
|
} |
|
result = fetchOidValue() ; |
|
} |
|
catch(BerException e) { |
|
next = backup ; |
|
throw e ; |
|
} |
|
|
|
return result ; |
|
} |
|
|
|
|
|
/** |
|
* Fetch a <CODE>NULL</CODE> value. |
|
* |
|
* @exception BerException Current position does not point to <CODE>NULL</CODE> value. |
|
*/ |
|
|
|
public void fetchNull() throws BerException { |
|
fetchNull(NullTag) ; |
|
} |
|
|
|
|
|
/** |
|
* Fetch a <CODE>NULL</CODE> value with a specified tag. |
|
* |
|
* @param tag The expected tag. |
|
* |
|
* @exception BerException Current position does not point to |
|
* <CODE>NULL</CODE> value or the tag is not the expected one. |
|
*/ |
|
|
|
public void fetchNull(int tag) throws BerException { |
|
final int backup = next ; |
|
try { |
|
if (fetchTag() != tag) { |
|
throw new BerException() ; |
|
} |
|
final int length = fetchLength(); |
|
if (length != 0) throw new BerException(); |
|
} |
|
catch(BerException e) { |
|
next = backup ; |
|
throw e ; |
|
} |
|
} |
|
|
|
|
|
|
|
/** |
|
* Fetch an <CODE>ANY</CODE> value. In fact, this method does not decode anything |
|
* it simply returns the next TLV as an array of bytes. |
|
* |
|
* @return The TLV as a byte array. |
|
* |
|
* @exception BerException The next TLV is really badly encoded... |
|
*/ |
|
|
|
public byte[] fetchAny() throws BerException { |
|
byte[] result = null ; |
|
final int backup = next ; |
|
try { |
|
final int tag = fetchTag() ; |
|
final int contentLength = fetchLength() ; |
|
if (contentLength < 0) throw new BerException() ; |
|
final int tlvLength = next + contentLength - backup ; |
|
if (contentLength > (bytes.length - next)) |
|
throw new IndexOutOfBoundsException("Decoded length exceeds buffer"); |
|
final byte[] data = new byte[tlvLength] ; |
|
java.lang.System.arraycopy(bytes,backup,data,0,tlvLength); |
|
// for (int i = 0 ; i < tlvLength ; i++) { |
|
// data[i] = bytes[backup + i] ; |
|
|
|
next = next + contentLength ; |
|
result = data; |
|
} |
|
catch(IndexOutOfBoundsException e) { |
|
next = backup ; |
|
throw new BerException() ; |
|
} |
|
// catch(Error e) { |
|
// debug("fetchAny: Error decoding BER: " + e); |
|
// throw e; |
|
// } |
|
|
|
return result ; |
|
} |
|
|
|
|
|
/** |
|
* Fetch an <CODE>ANY</CODE> value with a specific tag. |
|
* |
|
* @param tag The expected tag. |
|
* |
|
* @return The TLV as a byte array. |
|
* |
|
* @exception BerException The next TLV is really badly encoded... |
|
*/ |
|
|
|
public byte[] fetchAny(int tag) throws BerException { |
|
if (getTag() != tag) { |
|
throw new BerException() ; |
|
} |
|
return fetchAny() ; |
|
} |
|
|
|
|
|
|
|
/** |
|
* Fetch a sequence header. |
|
* The decoder computes the end position of the sequence and push it |
|
* on its stack. |
|
* |
|
* @exception BerException Current position does not point to a sequence header. |
|
*/ |
|
|
|
public void openSequence() throws BerException { |
|
openSequence(SequenceTag) ; |
|
} |
|
|
|
|
|
/** |
|
* Fetch a sequence header with a specific tag. |
|
* |
|
* @param tag The expected tag. |
|
* |
|
* @exception BerException Current position does not point to a sequence header |
|
* or the tag is not the expected one. |
|
*/ |
|
|
|
public void openSequence(int tag) throws BerException { |
|
final int backup = next ; |
|
try { |
|
if (fetchTag() != tag) { |
|
throw new BerException() ; |
|
} |
|
final int l = fetchLength() ; |
|
if (l < 0) throw new BerException(); |
|
if (l > (bytes.length - next)) throw new BerException(); |
|
stackBuf[stackTop++] = next + l ; |
|
} |
|
catch(BerException e) { |
|
next = backup ; |
|
throw e ; |
|
} |
|
} |
|
|
|
|
|
/** |
|
* Close a sequence. |
|
* The decode pull the stack and verifies that the current position |
|
* matches with the calculated end of the sequence. If not it throws |
|
* an exception. |
|
* |
|
* @exception BerException The sequence is not expected to finish here. |
|
*/ |
|
|
|
public void closeSequence() throws BerException { |
|
if (stackBuf[stackTop - 1] == next) { |
|
stackTop-- ; |
|
} |
|
else { |
|
throw new BerException() ; |
|
} |
|
} |
|
|
|
|
|
/** |
|
* Return <CODE>true</CODE> if the end of the current sequence is not reached. |
|
* When this method returns <CODE>false</CODE>, <CODE>closeSequence</CODE> can (and must) be |
|
* invoked. |
|
* |
|
* @return <CODE>true</CODE> if there is still some data in the sequence. |
|
*/ |
|
|
|
public boolean cannotCloseSequence() { |
|
return (next < stackBuf[stackTop - 1]) ; |
|
} |
|
|
|
|
|
/** |
|
* Get the tag of the data at the current position. |
|
* Current position is unchanged. |
|
* |
|
* @return The next tag. |
|
*/ |
|
|
|
public int getTag() throws BerException { |
|
int result = 0 ; |
|
final int backup = next ; |
|
try { |
|
result = fetchTag() ; |
|
} |
|
finally { |
|
next = backup ; |
|
} |
|
|
|
return result ; |
|
} |
|
|
|
|
|
|
|
public String toString() { |
|
final StringBuffer result = new StringBuffer(bytes.length * 2) ; |
|
for (int i = 0 ; i < bytes.length ; i++) { |
|
final int b = (bytes[i] > 0) ? bytes[i] : bytes[i] + 256 ; |
|
if (i == next) { |
|
result.append("(") ; |
|
} |
|
result.append(Character.forDigit(b / 16, 16)) ; |
|
result.append(Character.forDigit(b % 16, 16)) ; |
|
if (i == next) { |
|
result.append(")") ; |
|
} |
|
} |
|
if (bytes.length == next) { |
|
result.append("()") ; |
|
} |
|
|
|
return new String(result) ; |
|
} |
|
|
|
|
|
// |
|
// Some standard tags |
|
|
|
public final static int BooleanTag = 1 ; |
|
public final static int IntegerTag = 2 ; |
|
public final static int OctetStringTag = 4 ; |
|
public final static int NullTag = 5 ; |
|
public final static int OidTag = 6 ; |
|
public final static int SequenceTag = 0x30 ; |
|
|
|
|
|
|
|
|
|
////////////////////////// PRIVATE /////////////////////////////// |
|
|
|
|
|
|
|
/** |
|
* Fetch a tag and move the current position forward. |
|
* |
|
* @return The tag |
|
*/ |
|
|
|
private final int fetchTag() throws BerException { |
|
int result = 0 ; |
|
final int backup = next ; |
|
|
|
try { |
|
final byte b0 = bytes[next++] ; |
|
result = (b0 >= 0) ? b0 : b0 + 256 ; |
|
if ((result & 31) == 31) { |
|
while ((bytes[next] & 128) != 0) { |
|
result = result << 7 ; |
|
result = result | (bytes[next++] & 127); |
|
} |
|
} |
|
} |
|
catch(IndexOutOfBoundsException e) { |
|
next = backup ; |
|
throw new BerException() ; |
|
} |
|
|
|
return result ; |
|
} |
|
|
|
|
|
/** |
|
* Fetch a length and move the current position forward. |
|
* |
|
* @return The length |
|
*/ |
|
|
|
private final int fetchLength() throws BerException { |
|
int result = 0 ; |
|
final int backup = next ; |
|
|
|
try { |
|
final byte b0 = bytes[next++] ; |
|
if (b0 >= 0) { |
|
result = b0 ; |
|
} |
|
else { |
|
for (int c = 128 + b0 ; c > 0 ; c--) { |
|
final byte bX = bytes[next++] ; |
|
result = result << 8 ; |
|
result = result | ((bX >= 0) ? bX : bX+256) ; |
|
} |
|
} |
|
} |
|
catch(IndexOutOfBoundsException e) { |
|
next = backup ; |
|
throw new BerException() ; |
|
} |
|
|
|
return result ; |
|
} |
|
|
|
|
|
/** |
|
* Fetch an integer value and move the current position forward. |
|
* |
|
* @return The integer |
|
*/ |
|
|
|
private int fetchIntegerValue() throws BerException { |
|
int result = 0 ; |
|
final int backup = next ; |
|
|
|
try { |
|
final int length = fetchLength() ; |
|
if (length <= 0) throw new BerException() ; |
|
if (length > (bytes.length - next)) throw |
|
new IndexOutOfBoundsException("Decoded length exceeds buffer"); |
|
final int end = next + length ; |
|
result = bytes[next++] ; |
|
while (next < end) { |
|
final byte b = bytes[next++] ; |
|
if (b < 0) { |
|
result = (result << 8) | (256 + b) ; |
|
} |
|
else { |
|
result = (result << 8) | b ; |
|
} |
|
} |
|
} |
|
catch(BerException e) { |
|
next = backup ; |
|
throw e ; |
|
} |
|
catch(IndexOutOfBoundsException e) { |
|
next = backup ; |
|
throw new BerException() ; |
|
} |
|
catch(ArithmeticException e) { |
|
next = backup ; |
|
throw new BerException() ; |
|
} |
|
return result ; |
|
} |
|
|
|
|
|
/** |
|
* Fetch an integer value and return a long value. |
|
* FIX ME: someday we could have only on fetchIntegerValue() which always |
|
* returns a long value. |
|
* |
|
* @return The integer |
|
*/ |
|
|
|
private final long fetchIntegerValueAsLong() throws BerException { |
|
long result = 0 ; |
|
final int backup = next ; |
|
|
|
try { |
|
final int length = fetchLength() ; |
|
if (length <= 0) throw new BerException() ; |
|
if (length > (bytes.length - next)) throw |
|
new IndexOutOfBoundsException("Decoded length exceeds buffer"); |
|
|
|
final int end = next + length ; |
|
result = bytes[next++] ; |
|
while (next < end) { |
|
final byte b = bytes[next++] ; |
|
if (b < 0) { |
|
result = (result << 8) | (256 + b) ; |
|
} |
|
else { |
|
result = (result << 8) | b ; |
|
} |
|
} |
|
} |
|
catch(BerException e) { |
|
next = backup ; |
|
throw e ; |
|
} |
|
catch(IndexOutOfBoundsException e) { |
|
next = backup ; |
|
throw new BerException() ; |
|
} |
|
catch(ArithmeticException e) { |
|
next = backup ; |
|
throw new BerException() ; |
|
} |
|
return result ; |
|
} |
|
|
|
|
|
/** |
|
* Fetch a byte string and move the current position forward. |
|
* |
|
* @return The byte string |
|
*/ |
|
|
|
private byte[] fetchStringValue() throws BerException { |
|
byte[] result = null ; |
|
final int backup = next ; |
|
|
|
try { |
|
final int length = fetchLength() ; |
|
if (length < 0) throw new BerException() ; |
|
if (length > (bytes.length - next)) |
|
throw new IndexOutOfBoundsException("Decoded length exceeds buffer"); |
|
final byte data[] = new byte[length] ; |
|
java.lang.System.arraycopy(bytes,next,data,0,length); |
|
next += length; |
|
// int i = 0 ; |
|
// while (i < length) { |
|
// result[i++] = bytes[next++] ; |
|
|
|
result = data; |
|
} |
|
catch(BerException e) { |
|
next = backup ; |
|
throw e ; |
|
} |
|
catch(IndexOutOfBoundsException e) { |
|
next = backup ; |
|
throw new BerException() ; |
|
} |
|
catch(ArithmeticException e) { |
|
next = backup ; |
|
throw new BerException() ; |
|
} |
|
// catch(Error e) { |
|
// debug("fetchStringValue: Error decoding BER: " + e); |
|
// throw e; |
|
// } |
|
|
|
return result ; |
|
} |
|
|
|
|
|
|
|
/** |
|
* Fetch an oid and move the current position forward. |
|
* |
|
* @return The oid |
|
*/ |
|
|
|
private final long[] fetchOidValue() throws BerException { |
|
long[] result = null ; |
|
final int backup = next ; |
|
|
|
try { |
|
final int length = fetchLength() ; |
|
if (length <= 0) throw new BerException() ; |
|
if (length > (bytes.length - next)) |
|
throw new IndexOutOfBoundsException("Decoded length exceeds buffer"); |
|
// Count how many bytes have their 8th bit to 0 |
|
|
|
int subidCount = 2 ; |
|
for (int i = 1 ; i < length ; i++) { |
|
if ((bytes[next + i] & 0x80) == 0) { |
|
subidCount++ ; |
|
} |
|
} |
|
final int datalen = subidCount; |
|
final long[] data = new long[datalen]; |
|
final byte b0 = bytes[next++] ; |
|
|
|
// bugId 4641746 |
|
|
|
if (b0 < 0) throw new BerException(); |
|
|
|
// bugId 4641746 |
|
|
|
final long lb0 = b0 / 40 ; |
|
if (lb0 > 2) throw new BerException(); |
|
|
|
final long lb1 = b0 % 40; |
|
data[0] = lb0 ; |
|
data[1] = lb1 ; |
|
int i = 2 ; |
|
while (i < datalen) { |
|
long subid = 0 ; |
|
byte b = bytes[next++] ; |
|
while ((b & 0x80) != 0) { |
|
subid = (subid << 7) | (b & 0x7f) ; |
|
|
|
if (subid < 0) throw new BerException(); |
|
b = bytes[next++] ; |
|
} |
|
subid = (subid << 7) | b ; |
|
|
|
if (subid < 0) throw new BerException(); |
|
data[i++] = subid ; |
|
} |
|
result = data; |
|
} |
|
catch(BerException e) { |
|
next = backup ; |
|
throw e ; |
|
} |
|
catch(IndexOutOfBoundsException e) { |
|
next = backup ; |
|
throw new BerException() ; |
|
} |
|
// catch(Error e) { |
|
// debug("fetchOidValue: Error decoding BER: " + e); |
|
// throw e; |
|
// } |
|
|
|
return result ; |
|
} |
|
|
|
// private static final void debug(String str) { |
|
// System.out.println(str); |
|
// } |
|
|
|
// |
|
// This is the byte array containing the encoding. |
|
|
|
private final byte bytes[]; |
|
|
|
// |
|
// This is the current location. It is the next byte |
|
// to be decoded. It's an index in bytes[]. |
|
|
|
private int next = 0 ; |
|
|
|
// |
|
// This is the stack where end of sequences are kept. |
|
// A value is computed and pushed in it each time openSequence() |
|
// is invoked. |
|
// A value is pulled and checked each time closeSequence() is called. |
|
|
|
private final int stackBuf[] = new int[200] ; |
|
private int stackTop = 0 ; |
|
|
|
} |