001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Nam Nguyen
028: */
029:
030: package com.caucho.server.snmp;
031:
032: import java.io.IOException;
033: import java.io.InputStream;
034:
035: import com.caucho.server.snmp.types.*;
036: import com.caucho.util.L10N;
037:
038: public class SnmpParser {
039: private static final L10N L = new L10N(SnmpParser.class);
040:
041: private InputStream _is;
042: private int _bytesRead = 0;
043:
044: private boolean _isCheckIdentifier = true;
045:
046: public SnmpParser(InputStream is) {
047: _is = is;
048: }
049:
050: public SnmpParser(String s) {
051: _is = new StringInputStream(s);
052: }
053:
054: public SnmpMessageValue readMessage() throws IOException {
055: checkIdentifier(SnmpValue.SEQUENCE);
056:
057: int expectedLen = readLength();
058: int bytesRead = getBytesRead();
059:
060: IntegerValue version = readInteger();
061: OctetStringValue communityString = readOctetString();
062:
063: int id = readByte();
064: _isCheckIdentifier = false;
065:
066: PduValue pdu;
067: switch (id) {
068: case SnmpValue.GET_REQUEST_PDU:
069: pdu = readGetRequestPdu();
070: break;
071: case SnmpValue.GET_NEXT_REQUEST_PDU:
072: pdu = readGetNextRequestPdu();
073: break;
074: case SnmpValue.SET_REQUEST_PDU:
075: pdu = readSetRequestPdu();
076: break;
077: case SnmpValue.GET_RESPONSE_PDU:
078: pdu = readGetResponsePdu();
079: break;
080: //case SnmpValue.TRAP_PDU:
081: //break;
082: default:
083: throw new SnmpParsingException(L.l(
084: "unknown PDU type '{0}'", id));
085: }
086:
087: checkLength(expectedLen, getBytesRead() - bytesRead);
088:
089: return new SnmpMessageValue(version, communityString, pdu);
090: }
091:
092: public PduValue readGetRequestPdu() throws IOException {
093: return readPdu(SnmpValue.GET_REQUEST_PDU);
094: }
095:
096: public PduValue readGetNextRequestPdu() throws IOException {
097: return readPdu(SnmpValue.GET_NEXT_REQUEST_PDU);
098: }
099:
100: public PduValue readSetRequestPdu() throws IOException {
101: return readPdu(SnmpValue.SET_REQUEST_PDU);
102: }
103:
104: public PduValue readGetResponsePdu() throws IOException {
105: return readPdu(SnmpValue.GET_RESPONSE_PDU);
106: }
107:
108: private PduValue readPdu(int type) throws IOException {
109: checkIdentifier(type);
110:
111: int expectedLen = readLength();
112: int bytesRead = getBytesRead();
113:
114: IntegerValue requestId = readInteger();
115:
116: // errors are ignored for requests
117: IntegerValue error = readInteger();
118: IntegerValue errorIndex = readInteger();
119:
120: VarBindListValue varBindList = readVarBindList();
121:
122: checkLength(expectedLen, getBytesRead() - bytesRead);
123:
124: return PduValue.create(type, requestId, error, errorIndex,
125: varBindList);
126: }
127:
128: public VarBindListValue readVarBindList() throws IOException {
129: checkIdentifier(SnmpValue.SEQUENCE);
130:
131: int expectedLen = readLength();
132: int bytesRead = 0;
133:
134: VarBindListValue varBindList = new VarBindListValue();
135:
136: while (bytesRead < expectedLen) {
137: bytesRead -= getBytesRead();
138:
139: varBindList.addVarBind(readVarBind());
140:
141: bytesRead += getBytesRead();
142: }
143:
144: checkLength(expectedLen, bytesRead);
145:
146: return varBindList;
147: }
148:
149: public VarBindValue readVarBind() throws IOException {
150: checkIdentifier(SnmpValue.SEQUENCE);
151:
152: int expectedLen = readLength();
153: int bytesRead = getBytesRead();
154:
155: VarBindValue varBind = new VarBindValue(readObjectIdentifier(),
156: read());
157:
158: checkLength(expectedLen, getBytesRead() - bytesRead);
159:
160: return varBind;
161: }
162:
163: public void skipObject() throws IOException {
164: readByte();
165:
166: int len = readLength();
167:
168: for (int i = 0; i < len; i++) {
169: readByte();
170: }
171: }
172:
173: public SnmpValue read() throws IOException {
174: int identifier = readByte();
175:
176: _isCheckIdentifier = false;
177:
178: switch (identifier) {
179: case SnmpValue.NULL:
180: return readNull();
181: case SnmpValue.INTEGER:
182: return readInteger();
183: case SnmpValue.OCTET_STRING:
184: return readOctetString();
185: case SnmpValue.OBJECT_IDENTIFIER:
186: return readObjectIdentifier();
187: case SnmpValue.SEQUENCE:
188: return readSequence();
189: case SnmpValue.IP_ADDRESS:
190: return readIpAddress();
191: case SnmpValue.COUNTER:
192: return readCounter();
193: case SnmpValue.GAUGE:
194: return readGauge();
195: case SnmpValue.TIME_TICKS:
196: return readTimeTicks();
197: case SnmpValue.OPAQUE:
198: return readOpaque();
199: case SnmpValue.GET_REQUEST_PDU:
200: return readGetRequestPdu();
201: case SnmpValue.GET_NEXT_REQUEST_PDU:
202: return readGetNextRequestPdu();
203: case SnmpValue.GET_RESPONSE_PDU:
204: return readGetResponsePdu();
205: case SnmpValue.SET_REQUEST_PDU:
206: return readSetRequestPdu();
207: //case SnmpValue.TRAP_PDU:
208: // break;
209: default:
210: throw new SnmpParsingException(L.l(
211: "unknown identifier {0}", identifier));
212: }
213: }
214:
215: public NullValue readNull() throws IOException {
216: checkIdentifier(SnmpValue.NULL);
217: int len = readLength();
218:
219: if (len != 0)
220: throw new SnmpParsingException(
221: "length must be zero for NULL");
222:
223: return NullValue.NULL;
224: }
225:
226: public IntegerValue readInteger() throws IOException {
227: checkIdentifier(SnmpValue.INTEGER);
228: int len = readLength();
229:
230: if (len < 1 || len > 4)
231: throw new SnmpParsingException(L
232: .l("integer length {0} must be 1-4"));
233:
234: int value = readByte();
235: boolean isNeg = (value & 0x80) > 0;
236:
237: for (int i = 1; i < len; i++) {
238: value <<= 8;
239: value |= readByte();
240: }
241:
242: if (isNeg) {
243: while (len != 4) {
244: value |= 0xFF << (8 * len++);
245: }
246: }
247:
248: return new IntegerValue(value);
249: }
250:
251: public OctetStringValue readOctetString() throws IOException {
252: checkIdentifier(SnmpValue.OCTET_STRING);
253: int expectedLen = readLength();
254:
255: StringBuilder sb = new StringBuilder();
256:
257: for (int i = 0; i < expectedLen; i++) {
258: sb.append((char) readByte());
259: }
260:
261: return new OctetStringValue(sb.toString());
262: }
263:
264: public ObjectIdentifierValue readObjectIdentifier()
265: throws IOException {
266: checkIdentifier(SnmpValue.OBJECT_IDENTIFIER);
267: int len = readLength();
268:
269: StringBuilder sb = new StringBuilder();
270:
271: int total = 0;
272: while (len-- > 0) {
273: int b = readByte();
274:
275: total <<= 7;
276: total += (b & 0x7F);
277:
278: if ((b & 0x80) == 0) {
279: break;
280: }
281: }
282:
283: // total is (40x + y)
284: // x can only be 0,1,2
285: // y can not be more than 39 for x = 0,1
286:
287: int x;
288: int y;
289:
290: if (total < 40) {
291: x = 0;
292: y = total;
293: } else if (total < 80) {
294: x = 1;
295: y = total - 40;
296: } else {
297: x = 2;
298: y = total - 80;
299: }
300:
301: sb.append(x);
302: sb.append('.');
303: sb.append(y);
304:
305: while (len > 0) {
306: int val = 0;
307:
308: while (len-- > 0) {
309: int b = readByte();
310:
311: val <<= 7;
312: val += (b & 0x7F);
313:
314: if ((b & 0x80) == 0) {
315: break;
316: }
317: }
318:
319: sb.append('.');
320: sb.append(val);
321: }
322:
323: return new ObjectIdentifierValue(sb.toString());
324: }
325:
326: public IpAddressValue readIpAddress() throws IOException {
327: checkIdentifier(SnmpValue.IP_ADDRESS);
328: int expectedLen = readLength();
329:
330: StringBuilder sb = new StringBuilder();
331:
332: for (int i = 0; i < expectedLen; i++) {
333: int b = readByte();
334:
335: if (b < 0x00 || b > 0xFF) {
336: throw new SnmpParsingException(L.l(
337: "IP address digit {0} out of range", b));
338: }
339:
340: if (i != 0)
341: sb.append('.');
342:
343: sb.append(b);
344: }
345:
346: return new IpAddressValue(sb.toString());
347: }
348:
349: public CounterValue readCounter() throws IOException {
350: checkIdentifier(SnmpValue.COUNTER);
351: int expectedLen = readLength();
352:
353: long value = 0;
354:
355: for (int i = 0; i < expectedLen; i++) {
356: value <<= 8;
357: value |= readByte();
358: }
359:
360: return new CounterValue(value);
361: }
362:
363: public GaugeValue readGauge() throws IOException {
364: checkIdentifier(SnmpValue.GAUGE);
365: int expectedLen = readLength();
366:
367: long value = 0;
368:
369: for (int i = 0; i < expectedLen; i++) {
370: value <<= 8;
371: value |= readByte();
372: }
373:
374: return new GaugeValue(value);
375: }
376:
377: public TimeTicksValue readTimeTicks() throws IOException {
378: checkIdentifier(SnmpValue.TIME_TICKS);
379: int expectedLen = readLength();
380:
381: long value = 0;
382:
383: for (int i = 0; i < expectedLen; i++) {
384: value <<= 8;
385: value |= readByte();
386: }
387:
388: return new TimeTicksValue(value);
389: }
390:
391: public OpaqueValue readOpaque() throws IOException {
392: checkIdentifier(SnmpValue.OPAQUE);
393: int expectedLen = readLength();
394:
395: StringBuilder sb = new StringBuilder();
396:
397: for (int i = 0; i < expectedLen; i++) {
398: sb.append((char) readByte());
399: }
400:
401: return new OpaqueValue(sb.toString());
402: }
403:
404: public SequenceValue<SnmpValue> readSequence() throws IOException {
405: checkIdentifier(SnmpValue.SEQUENCE);
406: int expectedLen = readLength();
407:
408: SequenceValue<SnmpValue> sequence = new SequenceValue<SnmpValue>();
409:
410: int bytesRead = 0;
411: while (bytesRead < expectedLen) {
412: bytesRead -= getBytesRead();
413:
414: SnmpValue item = read();
415: sequence.add(item);
416:
417: bytesRead += getBytesRead();
418: }
419:
420: if (bytesRead != expectedLen) {
421: throw new SnmpParsingException(L.l(
422: "expected sequence length {0} != {1}", expectedLen,
423: bytesRead));
424: }
425:
426: return sequence;
427: }
428:
429: public int readLength() throws IOException {
430: int octlet = readByte();
431:
432: if ((octlet & 0x80) == 0)
433: return octlet & 0x7F;
434:
435: int numOfOctlets = octlet & 0x7F;
436:
437: int len = 0;
438: for (int i = 0; i < numOfOctlets; i++) {
439: octlet = readByte();
440:
441: len <<= 8;
442: len |= octlet;
443: }
444:
445: return len;
446: }
447:
448: private int readByte() throws IOException {
449: _bytesRead++;
450:
451: int ch = _is.read();
452:
453: if (ch < 0)
454: throw new SnmpParsingException("unexpected EOF");
455:
456: return ch;
457: }
458:
459: private int getBytesRead() {
460: return _bytesRead;
461: }
462:
463: private void checkLength(int expected, int bytesRead)
464: throws SnmpParsingException {
465: if (expected != bytesRead) {
466: throw new SnmpParsingException(L.l(
467: "expected length {0} != {1}", expected, bytesRead));
468: }
469: }
470:
471: private void checkIdentifier(int expected) throws IOException {
472: if (_isCheckIdentifier)
473: checkIdentifier(expected, readByte());
474:
475: _isCheckIdentifier = true;
476: }
477:
478: private void checkIdentifier(int expected, int identifier)
479: throws SnmpParsingException {
480: if (expected != identifier) {
481: throw new SnmpParsingException(L.l(
482: "saw '{0}' but expected type '{1}'",
483: type(identifier), type(expected)));
484: }
485: }
486:
487: private static String type(int identifier) {
488: return SnmpValue.typeName(identifier);
489: }
490:
491: static class StringInputStream extends InputStream {
492: private String _s;
493: private int _len;
494: private int _index;
495:
496: public StringInputStream(String s) {
497: _s = s;
498: _len = s.length();
499: }
500:
501: public int read() throws IOException {
502: if (_index < _len)
503: return _s.charAt(_index++);
504: else
505: return -1;
506: }
507: }
508: }
|