1 // NAME
2 // $RCSfile: AsnObject.java,v $
3 // DESCRIPTION
4 // [given below in javadoc format]
5 // DELTA
6 // $Revision: 3.30 $
7 // CREATED
8 // $Date: 2006/03/23 14:54:09 $
9 // COPYRIGHT
10 // Westhawk Ltd
11 // TO DO
12 //
13
14 /*
15 * Copyright (C) 1995, 1996 by West Consulting BV
16 *
17 * Permission to use, copy, modify, and distribute this software
18 * for any purpose and without fee is hereby granted, provided
19 * that the above copyright notices appear in all copies and that
20 * both the copyright notice and this permission notice appear in
21 * supporting documentation.
22 * This software is provided "as is" without express or implied
23 * warranty.
24 * author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
25 * original version by hargrave@dellgate.us.dell.com (Jordan Hargrave)
26 */
27
28 /*
29 * Copyright (C) 1996 - 2006 by Westhawk Ltd
30 * <a href="www.westhawk.co.uk">www.westhawk.co.uk</a>
31 *
32 * Permission to use, copy, modify, and distribute this software
33 * for any purpose and without fee is hereby granted, provided
34 * that the above copyright notices appear in all copies and that
35 * both the copyright notice and this permission notice appear in
36 * supporting documentation.
37 * This software is provided "as is" without express or implied
38 * warranty.
39 * author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
40 */
41
42 package uk.co.westhawk.snmp.stack;
43
44 /*-
45 * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
46 * SNMP Java Client
47 * ჻჻჻჻჻჻
48 * Copyright 2023 MetricsHub, Westhawk
49 * ჻჻჻჻჻჻
50 * This program is free software: you can redistribute it and/or modify
51 * it under the terms of the GNU Lesser General Public License as
52 * published by the Free Software Foundation, either version 3 of the
53 * License, or (at your option) any later version.
54 *
55 * This program is distributed in the hope that it will be useful,
56 * but WITHOUT ANY WARRANTY; without even the implied warranty of
57 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
58 * GNU General Lesser Public License for more details.
59 *
60 * You should have received a copy of the GNU General Lesser Public
61 * License along with this program. If not, see
62 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
63 * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
64 */
65
66 import java.io.*;
67 import java.util.*;
68 import uk.co.westhawk.snmp.util.*;
69
70 /**
71 * This class represents the ASN.1 base class.
72 * SMIv1 <a href="http://www.ietf.org/rfc/rfc1155.txt">RFC1155-SMI</a>.
73 * SMIv2 <a href="http://www.ietf.org/rfc/rfc2578.txt">SNMPv2-SMI</a>.
74 *
75 * <pre>
76 * <ASN Object> = <type> <length> <ASN Object>
77 * or
78 * <ASN Object> = <type> <length> <value>
79 * </pre>
80 *
81 * @author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
82 * @version $Revision: 3.30 $ $Date: 2006/03/23 14:54:09 $
83 */
84
85 public abstract class AsnObject extends Object implements SnmpConstants {
86 private static final String version_id = "@(#)$Id: AsnObject.java,v 3.30 2006/03/23 14:54:09 birgit Exp $ Copyright Westhawk Ltd";
87
88 /**
89 * Indicates the level of debug printed. By default this is none
90 * (0).
91 *
92 * @see #setDebug(int)
93 */
94 public static int debug = 0;
95
96 /**
97 * The type of object.
98 */
99 protected byte type;
100
101 /**
102 * The starting position of the AsnObject in the SNMP sequence. That
103 * is the position of the 'type' byte.
104 */
105 protected int startPos = 0;
106
107 /**
108 * The length of the header. That is the number of bytes of the 'type'
109 * and 'length' fields. Since there are multiple valid encodings for
110 * a given AsnObject, this length is not fixed!
111 */
112 protected int headerLength = 0;
113
114 /**
115 * The length of the actual contents. That is the same as the value of
116 * the 'length' field.
117 */
118 protected int contentsLength = 0;
119
120 /**
121 * Flag to signal the object is of a correct type.
122 */
123 protected boolean isCorrect = true;
124
125 abstract void write(OutputStream out, int pos)
126 throws IOException, EncodingException;
127
128 /**
129 * Returns a string representation of the object.
130 *
131 * @return The string
132 */
133 public abstract String toString();
134
135 /**
136 * Sets the new, global level of debug information for the stack package.
137 * The default value is zero, i.e. no debug info at all. All debug
138 * messages are printed to System.out.
139 * <p>
140 * The following messages will appear when debug is > (greater than):
141 * </p>
142 * <ul>
143 * <li><code>0</code> - IOExceptions that won't be thrown to the user</li>
144 * <li><code>1</code> - DecodingExceptions that won't be thrown to the user
145 * (received message with wrong SNMP version)</li>
146 * <li><code>2</code> - DecodingExceptions when trying to decode traps</li>
147 * <li><code>3</code> - processIncomingResponse errors: (corresponding Pdu
148 * cannot be found)</li>
149 * <li><code>4</code> - Discovery of EngineId, Timeline</li>
150 * <li><code>5</code> - Receiving traps, version/host do not correspond</li>
151 * <li><code>6</code> - Pdu messages, sending etc</li>
152 * <li><code>10</code> - Asn decoding of incoming messages</li>
153 * <li><code>12</code> - Transmitter messages, Socket messages</li>
154 * <li><code>15</code> - Not so important IOExceptions, like
155 * InterruptedIOException</li>
156 * </ul>
157 *
158 * @param newDebug the new debug value
159 */
160 public static void setDebug(int newDebug) {
161 debug = newDebug;
162 }
163
164 AsnObject AsnReadHeader(InputStream in) throws IOException {
165 return AsnReadHeader(in, 0);
166 }
167
168 /**
169 * @param pos The starting position, i.e. the pos the 'type' byte.
170 */
171 AsnObject AsnReadHeader(InputStream in, int pos) throws IOException {
172 int len, got;
173 AsnObject ret = null;
174 int had = in.available();
175
176 // Type byte
177 type = (byte) in.read();
178 if (type != -1) {
179 len = getLengthPacket(in);
180 int off = in.available();
181 int headLength = had - off;
182
183 got = 0;
184 byte body[] = new byte[len];
185 if (len > 0) {
186 got = in.read(body, 0, len);
187 }
188 if (got > -1) {
189 // Always decode, unless in.read returned an error
190 // (i.e. got == -1):
191 //
192 // 1.
193 // If we haven't received all bytes (i.e. got < len)
194 // we will still try and decode as much as we can:
195 // Since body[] has the length 'len' (i.e. the
196 // length according to the packet), it will be padded with
197 // zero
198 //
199 // 2.
200 // If len was 0, got would have been been -1, but since
201 // no read was performed, got is 0.
202 // A len of 0 is a valid case however.
203
204 ByteArrayInputStream buf = new ByteArrayInputStream(body);
205 ret = AsnMakeMe(buf, type, len, pos, headLength);
206
207 if (got != len) {
208 ret.isCorrect = false;
209 if (debug > 10) {
210 System.out.println("\nAsnObject.AsnReadHeader():"
211 + " incorrect packet. Length = " + (int) len
212 + ", Got = " + got);
213 }
214 }
215 } else {
216 if (debug > 10) {
217 System.out.println("\nAsnObject.AsnReadHeader(): Length = "
218 + (int) len
219 + ", Got = " + got
220 + ". Not processing any further.");
221 }
222 }
223 }
224 return ret;
225 }
226
227 AsnObject AsnMakeMe(InputStream in, byte t, int len, int pos,
228 int headLength) throws IOException {
229 AsnObject me = this;
230 type = t;
231
232 switch (type) {
233 case CONS_SEQ:
234 me = new AsnSequence(in, len, (pos + headLength));
235 break;
236 case GET_REQ_MSG:
237 case GETNEXT_REQ_MSG:
238 case SET_REQ_MSG:
239 case GETBULK_REQ_MSG:
240 case INFORM_REQ_MSG:
241 case GET_RSP_MSG:
242 case GET_RPRT_MSG:
243 case TRPV2_REQ_MSG:
244 me = new AsnPduSequence(in, len, (pos + headLength));
245 break;
246 case TRP_REQ_MSG:
247 me = new AsnTrapPduv1Sequence(in, len, (pos + headLength));
248 break;
249 case ASN_INTEGER:
250 me = new AsnInteger(in, len);
251 break;
252 case TIMETICKS:
253 case COUNTER:
254 case GAUGE:
255 case OBSOLETED_RFC1442_UINTEGER32:
256 me = new AsnUnsInteger(in, len);
257 break;
258 case COUNTER64:
259 me = new AsnUnsInteger64(in, len); // TODO test
260 break;
261 case ASN_OBJECT_ID:
262 me = new AsnObjectId(in, len);
263 break;
264 case IPADDRESS:
265 case ASN_OCTET_STR:
266 case OPAQUE:
267 case NSAP_ADDRESS:
268 me = new AsnOctets(in, len);
269 break;
270 case ASN_NULL:
271 me = new AsnNull(in, len);
272 break;
273 case SNMP_VAR_NOSUCHOBJECT:
274 case SNMP_VAR_NOSUCHINSTANCE:
275 case SNMP_VAR_ENDOFMIBVIEW:
276 me = new AsnPrimitive(type);
277 break;
278 default:
279 if (debug > 0) {
280 System.out.println("AsnObject.AsnMakeMe():"
281 + " Bad Type 0x" + SnmpUtilities.toHex(type));
282 }
283 me = new AsnNull(in, len);
284 me.isCorrect = false;
285 break;
286 }
287 me.type = type;
288 me.startPos = pos;
289 me.headerLength = headLength;
290 me.contentsLength = len;
291
292 if (debug > 10) {
293 System.out.println("AsnObject.AsnMakeMe():"
294 + " headerLength = " + me.headerLength
295 + ", contentsLength = " + me.contentsLength);
296 System.out.println("\ttype = 0x"
297 + SnmpUtilities.toHex(type)
298 + ", " + me.getRespTypeString()
299 + ", " + me.getClass().getName()
300 + ": value = " + me.toString()
301 + ", startPos = " + me.startPos
302 + ", correct = " + me.isCorrect);
303 }
304 return me;
305 }
306
307 /**
308 * Adds a child to the sequence
309 */
310 AsnObject add(AsnObject child) {
311 return child;
312 }
313
314 /**
315 * recursively look for a pduSequence object
316 * if not overridden - then not a sequence -
317 */
318 AsnObject findPdu() {
319 return null;
320 }
321
322 /**
323 * recursively look for a pduSequence object
324 * if not overridden - then not a sequence -
325 */
326 AsnObject findTrapPduv1() {
327 return null;
328 }
329
330 /**
331 * Output ASN header.
332 */
333 void AsnBuildHeader(OutputStream out, byte t, int length)
334 throws IOException {
335 int count;
336
337 type = t;
338 // Type byte
339 out.write(type);
340
341 // Length bytes
342 count = getLengthBytes(length);
343 headerLength = count + 1;
344 contentsLength = length;
345 if (debug > 10) {
346 System.out.println("AsnBuildHeader(): "
347 + "type = 0x" + SnmpUtilities.toHex(type)
348 + ", headerLength = " + headerLength
349 + ", contentsLength = " + contentsLength);
350 }
351
352 if (count > 1) {
353 // Write long form prefix byte
354 --count;
355 byte tmp = (byte) (0x80 | (byte) count);
356 out.write(tmp);
357 }
358 while (count != 0) {
359 // Write length bytes
360 out.write((byte) ((length >> (--count << 3)) & 0xFF));
361 }
362 }
363
364 int size() throws EncodingException {
365 return 0;
366 }
367
368 /**
369 * Returns length field size for a packet length.
370 * Returns:
371 * 1 if length will fit in short form (0x00-0x7f)
372 * 2+ if length >= 0x80
373 */
374 int getLengthBytes(int length) {
375 int mask, count;
376
377 if (length < 0x80) {
378 // Short form.. 1 byte
379 return 1;
380 } else {
381 // Long form.. prefix byte + length bytes
382 mask = 0xFF000000;
383 for (count = 4; (length & mask) == 0; count--) {
384 mask >>= 8;
385 }
386 return count + 1;
387 }
388 }
389
390 int getLengthPacket(InputStream in) throws IOException {
391 int length = 0;
392 byte mask = (byte) 0x7f;
393 byte len = (byte) in.read();
394
395 if ((0x80 & len) != 0) {
396 // long form
397
398 int count = (mask & len);
399 if (count < 4) {
400 byte data[] = new byte[count];
401 int n = in.read(data, 0, count);
402 if (n != count) {
403 throw new IOException("AsnObject.getLengthPacket(): Not enough data");
404 } else {
405 /*
406 * Thanks to Julien Conan (jconan@protego.net)
407 * for improving this code.
408 */
409 DataInputStream dis = new DataInputStream(
410 new ByteArrayInputStream(data));
411
412 for (n = 0; n < count; n++) {
413 length = (length << 8) + dis.readUnsignedByte();
414 }
415
416 }
417 }
418 } else {
419 // short form
420 length = (int) (mask & len);
421 }
422 return (length);
423 }
424
425 int getContentsPos() {
426 return startPos + headerLength;
427 }
428
429 int getContentsLength() {
430 return contentsLength;
431 }
432
433 /**
434 * Returns the type of this Asn object, such as ASN_INTEGER or
435 * IPADDRESS.
436 *
437 * Note, the name of this method is deceiving; It has
438 * nothing (more) to do with a response.
439 *
440 * @return The AsnObject type.
441 * @see #getRespTypeString()
442 * @see SnmpConstants
443 */
444 public byte getRespType() {
445 return type;
446 }
447
448 /**
449 * Returns the object type as string, such as "ASN_INTEGER" or
450 * "IPADDRESS".
451 *
452 * Note, the name of this method is deceiving; It has
453 * nothing (more) to do with a response.
454 *
455 * @return The AsnObject type.
456 * @see #getRespType()
457 * @see SnmpConstants
458 */
459 public String getRespTypeString() {
460 String str = "";
461 switch (type) {
462 case ASN_BOOLEAN:
463 str = "ASN_BOOLEAN";
464 break;
465 case ASN_INTEGER:
466 str = "ASN_INTEGER";
467 break;
468 case ASN_BIT_STR:
469 str = "ASN_BIT_STR";
470 break;
471 case ASN_OCTET_STR:
472 str = "ASN_OCTET_STR";
473 break;
474 case ASN_NULL:
475 str = "ASN_NULL";
476 break;
477 case ASN_OBJECT_ID:
478 str = "ASN_OBJECT_ID";
479 break;
480 case ASN_SEQUENCE:
481 str = "ASN_SEQUENCE";
482 break;
483 case ASN_SET:
484 str = "ASN_SET";
485 break;
486 case ASN_UNIVERSAL:
487 str = "ASN_UNIVERSAL";
488 break;
489 case ASN_PRIVATE:
490 str = "ASN_PRIVATE";
491 break;
492 case ASN_CONSTRUCTOR:
493 str = "ASN_CONSTRUCTOR";
494 break;
495 case ASN_EXTENSION_ID:
496 str = "ASN_EXTENSION_ID";
497 break;
498 case IPADDRESS:
499 str = "IPADDRESS";
500 break;
501 case COUNTER:
502 str = "COUNTER";
503 break;
504 case GAUGE:
505 str = "GAUGE";
506 break;
507 case TIMETICKS:
508 str = "TIMETICKS";
509 break;
510 case OPAQUE:
511 str = "OPAQUE";
512 break;
513 case NSAP_ADDRESS:
514 str = "NSAP_ADDRESS";
515 break;
516 case COUNTER64:
517 str = "COUNTER64";
518 break;
519 case OBSOLETED_RFC1442_UINTEGER32:
520 str = "OBSOLETED_RFC1442_UINTEGER32 (SMI v1)";
521 break;
522 case CONS_SEQ:
523 str = "CONS_SEQ";
524 break;
525 case SNMP_VAR_NOSUCHOBJECT:
526 str = "SNMP_VAR_NOSUCHOBJECT";
527 break;
528 case SNMP_VAR_NOSUCHINSTANCE:
529 str = "SNMP_VAR_NOSUCHINSTANCE";
530 break;
531 case SNMP_VAR_ENDOFMIBVIEW:
532 str = "SNMP_VAR_ENDOFMIBVIEW";
533 break;
534 case GET_REQ_MSG:
535 str = "GET_REQ_MSG";
536 break;
537 case GETNEXT_REQ_MSG:
538 str = "GETNEXT_REQ_MSG";
539 break;
540 case SET_REQ_MSG:
541 str = "SET_REQ_MSG";
542 break;
543 case GETBULK_REQ_MSG:
544 str = "GETBULK_REQ_MSG";
545 break;
546 case INFORM_REQ_MSG:
547 str = "INFORM_REQ_MSG";
548 break;
549 case GET_RSP_MSG:
550 str = "GET_RSP_MSG";
551 break;
552 case GET_RPRT_MSG:
553 str = "GET_RPRT_MSG";
554 break;
555 case TRP_REQ_MSG:
556 str = "TRP_REQ_MSG";
557 break;
558 case TRPV2_REQ_MSG:
559 str = "TRPV2_REQ_MSG";
560 break;
561 default:
562 str = "0x" + SnmpUtilities.toHex(type);
563 }
564 return str;
565 }
566 }