|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
package com.sun.jmx.snmp; |
|
|
|
|
|
/** |
|
* The <CODE>BerEncoder</CODE> class is used for encoding data using BER. |
|
* |
|
* A <CODE>BerEncoder</CODE> needs to be set up with a byte buffer. The encoded |
|
* data are stored in this byte buffer. |
|
* <P> |
|
* NOTE : the buffer is filled from end to start. This means the caller |
|
* needs to encode its data in the reverse order. |
|
* |
|
* |
|
* <p><b>This API is a Sun Microsystems internal API and is subject |
|
* to change without notice.</b></p> |
|
* |
|
* @since 1.5 |
|
*/ |
|
|
|
public class BerEncoder { |
|
|
|
/** |
|
* Constructs a new encoder and attaches it to the specified byte string. |
|
* |
|
* @param b The byte string containing the encoded data. |
|
*/ |
|
|
|
public BerEncoder(byte b[]) { |
|
bytes = b ; |
|
start = b.length ; |
|
stackTop = 0 ; |
|
} |
|
|
|
|
|
/** |
|
* Trim the encoding data and returns the length of the encoding. |
|
* |
|
* The encoder does backward encoding : so the bytes buffer is |
|
* filled from end to start. The encoded data must be shift before |
|
* the buffer can be used. This is the purpose of the <CODE>trim</CODE> method. |
|
* |
|
* After a call to the <CODE>trim</CODE> method, the encoder is reinitialized and <CODE>putXXX</CODE> |
|
* overwrite any existing encoded data. |
|
* |
|
* @return The length of the encoded data. |
|
*/ |
|
|
|
public int trim() { |
|
final int result = bytes.length - start ; |
|
|
|
// for (int i = start ; i < bytes.length ; i++) { |
|
// bytes[i-start] = bytes[i] ; |
|
|
|
if (result > 0) |
|
java.lang.System.arraycopy(bytes,start,bytes,0,result); |
|
|
|
start = bytes.length ; |
|
stackTop = 0 ; |
|
|
|
return result ; |
|
} |
|
|
|
/** |
|
* Put an integer. |
|
* |
|
* @param v The integer to encode. |
|
*/ |
|
|
|
public void putInteger(int v) { |
|
putInteger(v, IntegerTag) ; |
|
} |
|
|
|
|
|
/** |
|
* Put an integer with the specified tag. |
|
* |
|
* @param v The integer to encode. |
|
* @param tag The tag to encode. |
|
*/ |
|
|
|
public void putInteger(int v, int tag) { |
|
putIntegerValue(v) ; |
|
putTag(tag) ; |
|
} |
|
|
|
|
|
|
|
/** |
|
* Put an integer expressed as a long. |
|
* |
|
* @param v The long to encode. |
|
*/ |
|
|
|
public void putInteger(long v) { |
|
putInteger(v, IntegerTag) ; |
|
} |
|
|
|
|
|
/** |
|
* Put an integer expressed as a long with the specified tag. |
|
* |
|
* @param v The long to encode |
|
* @param tag The tag to encode. |
|
*/ |
|
|
|
public void putInteger(long v, int tag) { |
|
putIntegerValue(v) ; |
|
putTag(tag) ; |
|
} |
|
|
|
|
|
|
|
/** |
|
* Put an octet string. |
|
* |
|
* @param s The bytes to encode |
|
*/ |
|
|
|
public void putOctetString(byte[] s) { |
|
putOctetString(s, OctetStringTag) ; |
|
} |
|
|
|
|
|
/** |
|
* Put an octet string with a specified tag. |
|
* |
|
* @param s The bytes to encode |
|
* @param tag The tag to encode. |
|
*/ |
|
|
|
public void putOctetString(byte[] s, int tag) { |
|
putStringValue(s) ; |
|
putTag(tag) ; |
|
} |
|
|
|
|
|
/** |
|
* Put an object identifier. |
|
* |
|
* @param s The oid to encode. |
|
*/ |
|
|
|
public void putOid(long[] s) { |
|
putOid(s, OidTag) ; |
|
} |
|
|
|
|
|
/** |
|
* Put an object identifier with a specified tag. |
|
* |
|
* @param s The integer to encode. |
|
* @param tag The tag to encode. |
|
*/ |
|
|
|
public void putOid(long[] s, int tag) { |
|
putOidValue(s) ; |
|
putTag(tag) ; |
|
} |
|
|
|
|
|
/** |
|
* Put a <CODE>NULL</CODE> value. |
|
*/ |
|
|
|
public void putNull() { |
|
putNull(NullTag) ; |
|
} |
|
|
|
|
|
/** |
|
* Put a <CODE>NULL</CODE> value with a specified tag. |
|
* |
|
* @param tag The tag to encode. |
|
*/ |
|
|
|
public void putNull(int tag) { |
|
putLength(0) ; |
|
putTag(tag) ; |
|
} |
|
|
|
|
|
|
|
/** |
|
* Put an <CODE>ANY</CODE> value. In fact, this method does not encode anything. |
|
* It simply copies the specified bytes into the encoding. |
|
* |
|
* @param s The encoding of the <CODE>ANY</CODE> value. |
|
*/ |
|
|
|
public void putAny(byte[] s) { |
|
putAny(s, s.length) ; |
|
} |
|
|
|
|
|
/** |
|
* Put an <CODE>ANY</CODE> value. Only the first <CODE>byteCount</CODE> are considered. |
|
* |
|
* @param s The encoding of the <CODE>ANY</CODE> value. |
|
* @param byteCount The number of bytes of the encoding. |
|
*/ |
|
|
|
public void putAny(byte[] s, int byteCount) { |
|
java.lang.System.arraycopy(s,0,bytes,start-byteCount,byteCount); |
|
start -= byteCount; |
|
// for (int i = byteCount - 1 ; i >= 0 ; i--) { |
|
// bytes[--start] = s[i] ; |
|
// } |
|
} |
|
|
|
|
|
/** |
|
* Open a sequence. |
|
* The encoder push the current position on its stack. |
|
*/ |
|
|
|
public void openSequence() { |
|
stackBuf[stackTop++] = start ; |
|
} |
|
|
|
|
|
/** |
|
* Close a sequence. |
|
* The decode pull the stack to know the end of the current sequence. |
|
*/ |
|
|
|
public void closeSequence() { |
|
closeSequence(SequenceTag) ; |
|
} |
|
|
|
|
|
/** |
|
* Close a sequence with the specified tag. |
|
*/ |
|
|
|
public void closeSequence(int tag) { |
|
final int end = stackBuf[--stackTop] ; |
|
putLength(end - start) ; |
|
putTag(tag) ; |
|
} |
|
|
|
|
|
// |
|
// 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 ; |
|
|
|
|
|
|
|
|
|
////////////////////////// PROTECTED /////////////////////////////// |
|
|
|
|
|
|
|
/** |
|
* Put a tag and move the current position backward. |
|
* |
|
* @param tag The tag to encode. |
|
*/ |
|
|
|
protected final void putTag(int tag) { |
|
if (tag < 256) { |
|
bytes[--start] = (byte)tag ; |
|
} |
|
else { |
|
while (tag != 0) { |
|
bytes[--start] = (byte)(tag & 127) ; |
|
tag = tag << 7 ; |
|
} |
|
} |
|
} |
|
|
|
|
|
/** |
|
* Put a length and move the current position backward. |
|
* |
|
* @param length The length to encode. |
|
*/ |
|
|
|
protected final void putLength(final int length) { |
|
if (length < 0) { |
|
throw new IllegalArgumentException() ; |
|
} |
|
else if (length < 128) { |
|
bytes[--start] = (byte)length ; |
|
} |
|
else if (length < 256) { |
|
bytes[--start] = (byte)length ; |
|
bytes[--start] = (byte)0x81 ; |
|
} |
|
else if (length < 65536) { |
|
bytes[--start] = (byte)(length) ; |
|
bytes[--start] = (byte)(length >> 8) ; |
|
bytes[--start] = (byte)0x82 ; |
|
} |
|
else if (length < 16777126) { |
|
bytes[--start] = (byte)(length) ; |
|
bytes[--start] = (byte)(length >> 8) ; |
|
bytes[--start] = (byte)(length >> 16) ; |
|
bytes[--start] = (byte)0x83 ; |
|
} |
|
else { |
|
bytes[--start] = (byte)(length) ; |
|
bytes[--start] = (byte)(length >> 8) ; |
|
bytes[--start] = (byte)(length >> 16) ; |
|
bytes[--start] = (byte)(length >> 24) ; |
|
bytes[--start] = (byte)0x84 ; |
|
} |
|
} |
|
|
|
|
|
/** |
|
* Put an integer value and move the current position backward. |
|
* |
|
* @param v The integer to encode. |
|
*/ |
|
|
|
protected final void putIntegerValue(int v) { |
|
final int end = start ; |
|
int mask = 0x7f800000 ; |
|
int byteNeeded = 4 ; |
|
if (v < 0) { |
|
while (((mask & v) == mask) && (byteNeeded > 1)) { |
|
mask = mask >> 8 ; |
|
byteNeeded-- ; |
|
} |
|
} |
|
else { |
|
while (((mask & v) == 0) && (byteNeeded > 1)) { |
|
mask = mask >> 8 ; |
|
byteNeeded-- ; |
|
} |
|
} |
|
for (int i = 0 ; i < byteNeeded ; i++) { |
|
bytes[--start] = (byte)v ; |
|
v = v >> 8 ; |
|
} |
|
putLength(end - start) ; |
|
} |
|
|
|
|
|
/** |
|
* Put an integer value expressed as a long. |
|
* |
|
* @param v The integer to encode. |
|
*/ |
|
|
|
protected final void putIntegerValue(long v) { |
|
final int end = start ; |
|
long mask = 0x7f80000000000000L ; |
|
int byteNeeded = 8 ; |
|
if (v < 0) { |
|
while (((mask & v) == mask) && (byteNeeded > 1)) { |
|
mask = mask >> 8 ; |
|
byteNeeded-- ; |
|
} |
|
} |
|
else { |
|
while (((mask & v) == 0) && (byteNeeded > 1)) { |
|
mask = mask >> 8 ; |
|
byteNeeded-- ; |
|
} |
|
} |
|
for (int i = 0 ; i < byteNeeded ; i++) { |
|
bytes[--start] = (byte)v ; |
|
v = v >> 8 ; |
|
} |
|
putLength(end - start) ; |
|
} |
|
|
|
|
|
/** |
|
* Put a byte string and move the current position backward. |
|
* |
|
* @param s The byte string to encode. |
|
*/ |
|
|
|
protected final void putStringValue(byte[] s) { |
|
final int datalen = s.length; |
|
java.lang.System.arraycopy(s,0,bytes,start-datalen,datalen); |
|
start -= datalen; |
|
// for (int i = s.length - 1 ; i >= 0 ; i--) { |
|
// bytes[--start] = s[i] ; |
|
|
|
putLength(datalen) ; |
|
} |
|
|
|
|
|
|
|
/** |
|
* Put an oid and move the current position backward. |
|
* |
|
* @param s The oid to encode. |
|
*/ |
|
|
|
protected final void putOidValue(final long[] s) { |
|
final int end = start ; |
|
final int slength = s.length; |
|
|
|
|
|
if ((slength < 2) || (s[0] > 2) || (s[1] >= 40)) { |
|
throw new IllegalArgumentException() ; |
|
} |
|
for (int i = slength - 1 ; i >= 2 ; i--) { |
|
long c = s[i] ; |
|
if (c < 0) { |
|
throw new IllegalArgumentException() ; |
|
} |
|
else if (c < 128) { |
|
bytes[--start] = (byte)c ; |
|
} |
|
else { |
|
bytes[--start] = (byte)(c & 127) ; |
|
c = c >> 7 ; |
|
while (c != 0) { |
|
bytes[--start] = (byte)(c | 128) ; |
|
c = c >> 7 ; |
|
} |
|
} |
|
} |
|
bytes[--start] = (byte)(s[0] * 40 + s[1]) ; |
|
putLength(end - start) ; |
|
} |
|
|
|
|
|
// |
|
// This is the byte array containing the encoding. |
|
|
|
protected final byte bytes[]; |
|
|
|
// |
|
// This is the index of the first byte of the encoding. |
|
// It is initialized to <CODE>bytes.length</CODE> and decrease each time |
|
// an value is put in the encoder. |
|
|
|
protected int start = -1 ; |
|
|
|
// |
|
// This is the stack where end of sequences are kept. |
|
// A value is computed and pushed in it each time the <CODE>openSequence</CODE> method |
|
// is invoked. |
|
// A value is pulled and checked each time the <CODE>closeSequence</CODE> method is called. |
|
|
|
protected final int stackBuf[] = new int[200] ; |
|
protected int stackTop = 0 ; |
|
|
|
} |