001: /*
002: * SNMP Package
003: *
004: * Copyright (C) 2004, Jonathan Sevy <jsevy@mcs.drexel.edu>
005: *
006: * This is free software. Redistribution and use in source and binary forms, with
007: * or without modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright notice, this
011: * list of conditions and the following disclaimer.
012: * 2. Redistributions in binary form must reproduce the above copyright notice,
013: * this list of conditions and the following disclaimer in the documentation
014: * and/or other materials provided with the distribution.
015: * 3. The name of the author may not be used to endorse or promote products
016: * derived from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
019: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
021: * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
023: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
024: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
025: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
026: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: *
028: */
029:
030: package snmp;
031:
032: import java.util.*;
033: import java.io.*;
034:
035: /**
036: * Class representing ASN.1 object identifiers. These are unbounded sequences (arrays) of
037: * natural numbers, written as dot-separated strings.
038: */
039:
040: public class SNMPObjectIdentifier extends SNMPObject {
041: private long[] digits; // array of longs
042:
043: protected byte tag = SNMPBERCodec.SNMPOBJECTIDENTIFIER;
044:
045: /**
046: * Create a new empty object identifier (0-length array).
047: */
048:
049: public SNMPObjectIdentifier() {
050: digits = new long[0];
051: }
052:
053: /**
054: * Create a new object identifier from the supplied string of dot-separated nonegative
055: * decimal integer values.
056: * @throws SNMPBadValueException Indicates incorrectly-formatted string supplied.
057: */
058:
059: public SNMPObjectIdentifier(String digitString)
060: throws SNMPBadValueException {
061: convertDigitString(digitString);
062: }
063:
064: /**
065: * Create a new object identifier from the supplied array of nonegative
066: * integer values.
067: * @throws SNMPBadValueException Negative value(s) supplied.
068: */
069:
070: public SNMPObjectIdentifier(int[] digits)
071: throws SNMPBadValueException {
072: long[] longDigits = new long[digits.length];
073:
074: for (int i = 0; i < digits.length; i++) {
075: if (digits[i] < 0)
076: throw new SNMPBadValueException(
077: "Negative value supplied for SNMPObjectIdentifier.");
078:
079: longDigits[i] = digits[i];
080: }
081:
082: this .digits = longDigits;
083: }
084:
085: /**
086: * Create a new object identifier from the supplied array of nonegative
087: * long values.
088: * @throws SNMPBadValueException Negative value(s) supplied.
089: */
090:
091: public SNMPObjectIdentifier(long[] digits)
092: throws SNMPBadValueException {
093:
094: for (int i = 0; i < digits.length; i++) {
095: if (digits[i] < 0)
096: throw new SNMPBadValueException(
097: "Negative value supplied for SNMPObjectIdentifier.");
098: }
099:
100: this .digits = digits;
101: }
102:
103: /**
104: * Used to initialize from the BER encoding, as received in a response from
105: * an SNMP device responding to an SNMPGetRequest.
106: * @throws SNMPBadValueException Indicates an invalid BER encoding supplied. Shouldn't
107: * occur in normal operation, i.e., when valid responses are received from devices.
108: */
109:
110: protected SNMPObjectIdentifier(byte[] enc)
111: throws SNMPBadValueException {
112: extractFromBEREncoding(enc);
113: }
114:
115: /**
116: * Return array of integers corresponding to components of identifier.
117: */
118:
119: public Object getValue() {
120: return digits;
121: }
122:
123: /**
124: * Used to set the value from an integer or long array containing the identifier components, or from
125: * a String containing a dot-separated sequence of nonegative values.
126: * @throws SNMPBadValueException Indicates an incorrect object type supplied, or negative array
127: * elements, or an incorrectly formatted String.
128: */
129:
130: public void setValue(Object digits) throws SNMPBadValueException {
131: if (digits instanceof long[]) {
132: for (int i = 0; i < ((long[]) digits).length; i++) {
133: if (((long[]) digits)[i] < 0)
134: throw new SNMPBadValueException(
135: "Negative value supplied for SNMPObjectIdentifier.");
136: }
137:
138: this .digits = (long[]) digits;
139: } else if (digits instanceof int[]) {
140: long[] longDigits = new long[((int[]) digits).length];
141:
142: for (int i = 0; i < ((int[]) digits).length; i++) {
143: if (((int[]) digits)[i] < 0)
144: throw new SNMPBadValueException(
145: "Negative value supplied for SNMPObjectIdentifier.");
146:
147: longDigits[i] = ((int[]) digits)[i];
148: }
149:
150: this .digits = longDigits;
151: } else if (digits instanceof String) {
152: convertDigitString((String) digits);
153: } else
154: throw new SNMPBadValueException(
155: " Object Identifier: bad object supplied to set value ");
156: }
157:
158: /**
159: * Return BER encoding for this object identifier.
160: */
161:
162: protected byte[] getBEREncoding() {
163: ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
164:
165: byte type = SNMPBERCodec.SNMPOBJECTIDENTIFIER;
166:
167: // write contents of array of values
168: byte[] data = encodeArray();
169:
170: // calculate encoding for length of data
171: byte[] len = SNMPBERCodec.encodeLength(data.length);
172:
173: // encode T,L,V info
174: outBytes.write(type);
175: outBytes.write(len, 0, len.length);
176: outBytes.write(data, 0, data.length);
177:
178: return outBytes.toByteArray();
179: }
180:
181: private byte[] encodeArray() {
182: ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
183:
184: int numElements = digits.length;
185:
186: // encode first two identifier digits as one byte, using the 40*x + y rule;
187: // of course, if only one element, just use 40*x; if none, do nothing
188: if (numElements >= 2) {
189: outBytes.write((byte) (40 * digits[0] + digits[1]));
190: } else if (numElements == 1) {
191: outBytes.write((byte) (40 * digits[0]));
192: }
193:
194: for (int i = 2; i < numElements; ++i) {
195: byte[] nextBytes = encodeValue(digits[i]);
196: outBytes.write(nextBytes, 0, nextBytes.length);
197: }
198:
199: return outBytes.toByteArray();
200: }
201:
202: private byte[] encodeValue(long v) {
203: // see how many bytes are needed: each value uses just
204: // 7 bits of each byte, with high-order bit functioning as
205: // a continuation marker
206: int numBytes = 0;
207: long temp = v;
208:
209: do {
210: ++numBytes;
211: temp = (long) Math.floor(temp / 128);
212: } while (temp > 0);
213:
214: byte[] enc = new byte[numBytes];
215: // encode lowest-order byte, without setting high bit
216: enc[numBytes - 1] = (byte) (v % 128);
217: v = (long) Math.floor(v / 128);
218:
219: //.encode other bytes with high bit set
220: for (int i = numBytes - 2; i >= 0; --i) {
221: enc[i] = (byte) ((v % 128) + 128);
222: v = (long) Math.floor(v / 128);
223: }
224:
225: return enc;
226: }
227:
228: private void convertDigitString(String digitString)
229: throws SNMPBadValueException {
230: try {
231: StringTokenizer st = new StringTokenizer(digitString, " .");
232: int size = 0;
233:
234: while (st.hasMoreTokens()) {
235: // figure out how many values are in string
236: size++;
237: st.nextToken();
238: }
239:
240: long[] returnDigits = new long[size];
241:
242: st = new StringTokenizer(digitString, " .");
243:
244: for (int i = 0; i < size; i++) {
245: returnDigits[i] = Long.parseLong(st.nextToken());
246: if (returnDigits[i] < 0)
247: throw new SNMPBadValueException(
248: " Object Identifier: bad string supplied to set value ");
249: }
250:
251: digits = returnDigits;
252:
253: } catch (NumberFormatException e) {
254: throw new SNMPBadValueException(
255: " Object Identifier: bad string supplied for object identifier value ");
256: }
257:
258: }
259:
260: private void extractFromBEREncoding(byte[] enc)
261: throws SNMPBadValueException {
262: // note: masks must be ints; byte internal representation issue(?)
263: int bitTest = 0x80; // test for leading 1
264: int highBitMask = 0x7F; // mask out high bit for value
265:
266: // first, compute number of "digits";
267: // will just be number of bytes with leading 0's
268: int numInts = 0;
269: for (int i = 0; i < enc.length; i++) {
270: if ((enc[i] & bitTest) == 0) //high-order bit not set; count
271: numInts++;
272: }
273:
274: if (numInts > 0) {
275: // create new int array to hold digits; since first value is 40*x + y,
276: // need one extra entry in array to hold this.
277: digits = new long[numInts + 1];
278:
279: int currentByte = -1; // will be incremented to 0
280:
281: long value = 0;
282:
283: // read in values 'til get leading 0 in byte
284: do {
285: currentByte++;
286: value = value * 128 + (enc[currentByte] & highBitMask);
287: } while ((enc[currentByte] & bitTest) > 0); // implies high bit set!
288:
289: // now handle 40a + b
290: digits[0] = (long) Math.floor(value / 40);
291: digits[1] = value % 40;
292:
293: // now read in rest!
294: for (int i = 2; i < numInts + 1; i++) {
295: // read in values 'til get leading 0 in byte
296: value = 0;
297: do {
298: currentByte++;
299: value = value * 128
300: + (enc[currentByte] & highBitMask);
301: } while ((enc[currentByte] & bitTest) > 0);
302:
303: digits[i] = value;
304: }
305:
306: } else {
307: // no digits; create empty digit array
308: digits = new long[0];
309: }
310:
311: }
312:
313: /*
314: public boolean equals(SNMPObjectIdentifier other)
315: {
316: long[] otherDigits = (long[])(other.getValue());
317:
318: boolean areEqual = true;
319:
320: if (digits.length != otherDigits.length)
321: {
322: areEqual = false;
323: }
324: else
325: {
326: for (int i = 0; i < digits.length; i++)
327: {
328: if (digits[i] != otherDigits[i])
329: {
330: areEqual = false;
331: break;
332: }
333: }
334: }
335:
336: return areEqual;
337:
338: }
339: */
340:
341: /**
342: * Checks the internal arrays for equality.
343: */
344:
345: public boolean equals(Object other) {
346: // false if other is null
347: if (other == null) {
348: return false;
349: }
350:
351: // check first to see that they're both of the same class
352: if (!this .getClass().equals(other.getClass())) {
353: return false;
354: }
355:
356: SNMPObjectIdentifier otherSNMPObject = (SNMPObjectIdentifier) other;
357:
358: // see if their embedded arrays are equal
359: if (java.util.Arrays.equals((long[]) this .getValue(),
360: (long[]) otherSNMPObject.getValue())) {
361: return true;
362: } else {
363: return false;
364: }
365: }
366:
367: /**
368: * Generates a hash value so SNMP Object Identifiers can be used in Hashtables.
369: */
370:
371: public int hashCode() {
372: int hash = 0;
373:
374: // generate a hashcode from the embedded array
375: for (int i = 0; i < digits.length; i++) {
376: hash += (int) (digits[i] ^ (digits[i] >> 32));
377: hash += (hash << 10);
378: hash ^= (hash >> 6);
379: }
380:
381: hash += (hash << 3);
382: hash ^= (hash >> 11);
383: hash += (hash << 15);
384:
385: return hash;
386: }
387:
388: /**
389: * Return dot-separated sequence of decimal values.
390: */
391:
392: public String toString() {
393: StringBuffer valueStringBuffer = new StringBuffer();
394: if (digits.length > 0) {
395: valueStringBuffer.append(digits[0]);
396:
397: for (int i = 1; i < digits.length; ++i) {
398: valueStringBuffer.append(".");
399: valueStringBuffer.append(digits[i]);
400: }
401: }
402:
403: return valueStringBuffer.toString();
404: }
405:
406: }
|