View Javadoc
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   * &lt;ASN Object&gt; = &lt;type&gt; &lt;length&gt; &lt;ASN Object&gt;
77   * or
78   * &lt;ASN Object&gt; = &lt;type&gt; &lt;length&gt; &lt;value&gt;
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 &gt; (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 }