View Javadoc
1   // NAME
2   //      $RCSfile: AsnOctets.java,v $
3   // DESCRIPTION
4   //      [given below in javadoc format]
5   // DELTA
6   //      $Revision: 3.39 $
7   // CREATED
8   //      $Date: 2006/03/23 14:54:10 $
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 uk.co.westhawk.snmp.util.*;
67  import java.io.*;
68  import java.util.*;
69  import java.net.InetAddress;
70  import java.text.SimpleDateFormat;
71  
72  /**
73   * This class represents the ASN.1 Octet class.
74   * It can be used for Octets, Ip Addresses and Opaque primitive types. 
75   *
76   * The Octets type (ASN_OCTET_STR) is used for some text convensions. 
77   * This class supports the DateAndTime, DisplayString and
78   * InternationalDisplayString and Ipv6Address text convensions. 
79   * <br/>
80   * Note, the SNMP representation of IPv4 and IPv6 is different:
81   * <ul>
82   *    <li>IPv4: IPADDRESS (or ASN_OCTET_STR, see rfc 4001)</li>
83   *    <li>IPv6: ASN_OCTET_STR</li>
84   * </ul>
85   * See also 
86   * <a href="http://www.ietf.org/rfc/rfc2465.txt">IPV6-TC</a>,
87   * <a href="http://www.ietf.org/rfc/rfc3416.txt">SNMPv2-PDU</a>,
88   * <a href="http://www.ietf.org/rfc/rfc4001.txt">INET-ADDRESS-MIB</a>.
89   *
90   * @see SnmpConstants#ASN_OCTET_STR
91   * @see SnmpConstants#IPADDRESS
92   * @see SnmpConstants#OPAQUE
93   *
94   * @author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
95   * @version $Revision: 3.39 $ $Date: 2006/03/23 14:54:10 $
96   */
97  public class AsnOctets extends AsnObject {
98      private static final String version_id = "@(#)$Id: AsnOctets.java,v 3.39 2006/03/23 14:54:10 birgit Exp $ Copyright Westhawk Ltd";
99  
100     /**
101      * The hexadecimal prefix that is used when printing a hexadecimal
102      * number in toString(). By default this is "0x".
103      */
104     public static String HEX_PREFIX = "0x";
105 
106     /**
107      * The object that is used in toCalendar() to format the calendar
108      * representation of the Octets according to the DateAndTime text
109      * convension.
110      * The pattern is "yyyy-M-d,HH:mm:ss.SS,z".
111      *
112      * @see #getCalendar()
113      * @see #toCalendar()
114      * @see SimpleDateFormat
115      */
116     public static SimpleDateFormat CALFORMAT = new SimpleDateFormat("yyyy-M-d,HH:mm:ss.SS,z");
117 
118     /**
119      * The default AsnOctetsPrintableFace object.
120      *
121      * @see #setPrintable
122      * @see DefaultAsnOctetsPrintable
123      */
124     public static AsnOctetsPrintableFace printableObject = new DefaultAsnOctetsPrintable();
125 
126     byte value[];
127 
128     /** Cache the hash code for the OID */
129     private int hash = 0;
130 
131     /**
132      * Constructor. The type of the AsnOctets defaults to ASN_OCTET_STR.
133      *
134      * @param s The byte array representing the AsnOctets
135      * @see SnmpConstants#ASN_OCTET_STR
136      */
137     public AsnOctets(byte s[])
138             throws IllegalArgumentException {
139         this(s, ASN_OCTET_STR);
140     }
141 
142     /**
143      * Constructor to create a specific type of AsnOctets.
144      *
145      * @param s The byte array representing the AsnOctets
146      * @param t The type of the AsnOctets
147      * @see SnmpConstants#ASN_OCTET_STR
148      * @see SnmpConstants#IPADDRESS
149      * @see SnmpConstants#OPAQUE
150      */
151     public AsnOctets(byte s[], byte t)
152             throws IllegalArgumentException {
153         value = s;
154         type = t;
155         if (value == null) {
156             throw new IllegalArgumentException("Value is null");
157         }
158     }
159 
160     /**
161      * Constructor. The type of the AsnOctets defaults to ASN_OCTET_STR.
162      *
163      * @param s The character array representing the AsnOctets
164      * @see SnmpConstants#ASN_OCTET_STR
165      */
166     public AsnOctets(char s[]) {
167         int idx;
168 
169         value = new byte[s.length];
170         type = ASN_OCTET_STR;
171         for (idx = 0; idx < s.length; idx++) {
172             value[idx] = (byte) s[idx];
173         }
174     }
175 
176     /**
177      * Constructor. The type of the AsnOctets defaults to ASN_OCTET_STR.
178      *
179      * @param s The string representing the AsnOctets
180      * @see SnmpConstants#ASN_OCTET_STR
181      */
182     public AsnOctets(String s) {
183         this(s.toCharArray());
184     }
185 
186     /**
187      * Constructor to create an ASN IP Address.
188      * If the address represents an IPv4 address, the asn type will be
189      * set to IPADDRESS. If it represents an IPv6 address, the asn type
190      * will be set to ASN_OCTET_STR.
191      *
192      * <br/>
193      * Note, the SNMP representation of IPv4 and IPv6 is different:
194      * <ul>
195      * <li>IPv4: IPADDRESS (or ASN_OCTET_STR, see rfc 4001)</li>
196      * <li>IPv6: ASN_OCTET_STR</li>
197      * </ul>
198      * See also
199      * <a href="http://www.ietf.org/rfc/rfc2465.txt">IPV6-TC</a>,
200      * <a href="http://www.ietf.org/rfc/rfc3416.txt">SNMPv2-PDU</a>,
201      * <a href="http://www.ietf.org/rfc/rfc4001.txt">INET-ADDRESS-MIB</a>.
202      *
203      * @param iad The Inet Address
204      *
205      * @see #AsnOctets(Inet4Address, byte)
206      */
207     public AsnOctets(InetAddress iad)
208             throws IllegalArgumentException {
209         this(iad.getAddress(), ASN_OCTET_STR);
210         if (iad instanceof java.net.Inet4Address) {
211             // IPv4
212             type = IPADDRESS;
213         } else {
214             // IPv6 is ASN_OCTET_STR, so do nothing
215         }
216     }
217 
218     /**
219      * Constructor to create an ASN IPv4 Address.
220      * If the address is an IPv4 address, it can either be represented
221      * by IPADDRESS or as ASN_OCTET_STR.
222      *
223      * See also
224      * <a href="http://www.ietf.org/rfc/rfc2465.txt">IPV6-TC</a>,
225      * <a href="http://www.ietf.org/rfc/rfc3416.txt">SNMPv2-PDU</a>,
226      * <a href="http://www.ietf.org/rfc/rfc4001.txt">INET-ADDRESS-MIB</a>.
227      *
228      * @param iad The IPv4 Inet Address
229      * @param t   The type of the AsnOctets
230      *
231      * @see #AsnOctets(InetAddress)
232      * @see SnmpConstants#IPADDRESS
233      * @see SnmpConstants#ASN_OCTET_STR
234      * @since 4_14
235      */
236     public AsnOctets(java.net.Inet4Address iad, byte t)
237             throws IllegalArgumentException {
238         this(iad.getAddress(), t);
239     }
240 
241     /**
242      * Constructor for DateAndTime text convension.
243      * See
244      * <a href="http://www.ietf.org/rfc/rfc2579.txt">SNMPv2-TC</a>
245      *
246      * <pre>
247      *      field  octets  contents                  range
248      *      -----  ------  --------                  -----
249      *        1      1-2   year*                     0..65536
250      *        2       3    month                     1..12
251      *        3       4    day                       1..31
252      *        4       5    hour                      0..23
253      *        5       6    minutes                   0..59
254      *        6       7    seconds                   0..60
255      *                     (use 60 for leap-second)
256      *        7       8    deci-seconds              0..9
257      *
258      *        8       9    direction from UTC        '+' / '-'
259      *        9      10    hours from UTC*           0..13
260      *       10      11    minutes from UTC          0..59
261      *
262      * SYNTAX       OCTET STRING (SIZE (8 | 11))
263      * </pre>
264      *
265      * @since 4_14
266      */
267     public AsnOctets(Calendar cal) {
268         value = new byte[11];
269         type = ASN_OCTET_STR;
270 
271         int year = cal.get(Calendar.YEAR);
272         // Calendar: 0=January
273         int month = cal.get(Calendar.MONTH) + 1;
274         int day = cal.get(Calendar.DAY_OF_MONTH);
275         int hour = cal.get(Calendar.HOUR_OF_DAY);
276         int min = cal.get(Calendar.MINUTE);
277         int sec = cal.get(Calendar.SECOND);
278         int msec = cal.get(Calendar.MILLISECOND);
279         int msecGMT = cal.get(Calendar.ZONE_OFFSET);
280 
281         // The value of year is in network-byte order
282         // Is this correct?
283         value[0] = (byte) ((year / 256) % 256);
284         value[1] = (byte) (year % 256);
285 
286         value[2] = (byte) (month & 0xFF);
287         value[3] = (byte) (day & 0xFF);
288         value[4] = (byte) (hour & 0xFF);
289         value[5] = (byte) (min & 0xFF);
290         value[6] = (byte) (sec & 0xFF);
291         value[7] = (byte) ((msec / 100) & 0xFF);
292 
293         char dir = '\0';
294         if (msecGMT < 0) {
295             dir = '-';
296             msecGMT = msecGMT * -1;
297         } else {
298             dir = '+';
299         }
300         value[8] = (byte) dir;
301 
302         if (msecGMT == 0) {
303             value[9] = 0x00;
304             value[10] = 0x00;
305         } else {
306             int minGMT = (int) (((double) msecGMT) / 1000.0 / 60.0);
307             if (minGMT == 0) {
308                 value[9] = 0x00;
309                 value[10] = 0x00;
310             } else {
311                 int hourGMT = (int) (minGMT / 60.0);
312                 minGMT = minGMT - (hourGMT * 60);
313                 value[9] = (byte) (hourGMT & 0xFF);
314                 value[10] = (byte) (minGMT & 0xFF);
315             }
316         }
317     }
318 
319     /**
320      * Constructor.
321      *
322      * @param in  The input stream from which the value should be read
323      * @param len The length of the AsnOctets
324      */
325     public AsnOctets(InputStream in, int len) throws IOException {
326         value = new byte[len];
327         if (len != 0) {
328             if (len == in.read(value, 0, len)) {
329                 String str = "";
330                 // str = new String(value);
331             } else {
332                 throw new IOException("AsnOctets(): Not enough data");
333             }
334         } else {
335             // if len is zero, the in.read will return -1
336             // a length of zero is a valid case.
337             ;
338         }
339     }
340 
341     /**
342      * Sets the global hexadecimal prefix. This prefix will be used in
343      * toString() when it prints out a hexadecimal number. It is not
344      * used in toHex(). The default is "0x".
345      *
346      * @see #toString()
347      * @see #toHex()
348      * @see #HEX_PREFIX
349      */
350     public static void setHexPrefix(String newPrefix) {
351         HEX_PREFIX = newPrefix;
352     }
353 
354     /**
355      * Sets the global AsnOctetsPrintableFace printableObject. This
356      * object will be used in the toString() and the
357      * toInternationalDisplayString() methods.
358      *
359      * @see #toString
360      * @see #toInternationalDisplayString
361      * @since 4_14
362      */
363     public static void setPrintable(AsnOctetsPrintableFace obj) {
364         if (obj != null) {
365             printableObject = obj;
366         }
367     }
368 
369     /**
370      * Returns the String value. Calls toString().
371      *
372      * @return The value of the AsnOctets
373      * @see #toString()
374      */
375     public String getValue() {
376         return toString();
377     }
378 
379     /**
380      * Returns the bytes. This returns a copy of the internal byte array.
381      *
382      * @return The bytes of the AsnOctets
383      */
384     public byte[] getBytes() {
385         int len = value.length;
386         byte[] bytea = new byte[len];
387         System.arraycopy(value, 0, bytea, 0, len);
388         return bytea;
389     }
390 
391     /**
392      * Returns the string representation of the AsnOctets.
393      * <p>
394      * The string will have one of the following formats:
395      * </p>
396      * <ul>
397      * <li>if this class represents an IP Address (v4), it will call
398      * toIpAddress()</li>
399      * <li>&lt;prefix&gt;aa[:bb]*, if this class represents a non-printable
400      * string or has type OPAQUE.
401      * The output will be in hexadecimal numbers (see toHex()). It will be prefixed
402      * according to the hex. prefix value</li>
403      * <li>a printable string, if this class seems printable</li>
404      * </ul>
405      *
406      * <p>
407      * When the type is ASN_OCTET_STR, this method uses the
408      * AsnOctetsPrintableFace.isPrintable() to determine whether or not
409      * the string is printable. If it is printable, it will use
410      * AsnOctetsPrintableFace.toInternationalDisplayString() to
411      * transform the Octets to a String.
412      * </p>
413      *
414      * <br/>
415      * Note, the SNMP representation of IPv4 and IPv6 is different:
416      * <ul>
417      * <li>IPv4: IPADDRESS (or ASN_OCTET_STR, see rfc 4001)</li>
418      * <li>IPv6: ASN_OCTET_STR</li>
419      * </ul>
420      * See also
421      * <a href="http://www.ietf.org/rfc/rfc2465.txt">IPV6-TC</a>,
422      * <a href="http://www.ietf.org/rfc/rfc3416.txt">SNMPv2-PDU</a>,
423      * <a href="http://www.ietf.org/rfc/rfc4001.txt">INET-ADDRESS-MIB</a>.
424      *
425      * @see #HEX_PREFIX
426      * @see #setHexPrefix(String)
427      * @see #toHex
428      * @see #toIpAddress
429      * @see AsnOctetsPrintableFace#isPrintable
430      * @see AsnOctetsPrintableFace#toInternationalDisplayString
431      * @return The string representation of the AsnOctets
432      */
433     public String toString() {
434         return toString(printableObject);
435     }
436 
437     /**
438      * As toString(), but this methods will use this specific, one-off
439      * AsnOctetsPrintableFace object.
440      *
441      * @see #toString()
442      * @since 4_14
443      */
444     public String toString(AsnOctetsPrintableFace face) {
445         String str = "";
446 
447         if (type == IPADDRESS) {
448             // for IPv4 only
449             str = toIpAddress();
450         } else if (type == OPAQUE) {
451             str = HEX_PREFIX + toHex();
452         } else {
453             boolean isPrintable = face.isPrintable(value);
454             if (isPrintable) {
455                 str = face.toInternationalDisplayString(value);
456             } else {
457                 str = HEX_PREFIX + toHex();
458             }
459         }
460         return str;
461     }
462 
463     int size() {
464         return value.length;
465     }
466 
467     void write(OutputStream out, int pos) throws IOException {
468         int idx;
469 
470         // Output header
471         AsnBuildHeader(out, type, value.length);
472         if (debug > 10) {
473             System.out.println("\tAsnOctets(): value = " + toString()
474                     + ", pos = " + pos);
475         }
476 
477         // Output data
478         for (idx = 0; idx < value.length; idx++) {
479             out.write(value[idx]);
480         }
481     }
482 
483     /**
484      * Returns this Octet as an IP Address string. The format is
485      * aaa.bbb.ccc.ddd (IPv4) or a:b:c:d:e:f:g:h (IPv6).
486      *
487      * Note, the SNMP representation of IPv4 and IPv6 is different:
488      * <ul>
489      * <li>IPv4: IPADDRESS (or ASN_OCTET_STR, see rfc 4001)</li>
490      * <li>IPv6: ASN_OCTET_STR</li>
491      * </ul>
492      * See also
493      * <a href="http://www.ietf.org/rfc/rfc2465.txt">IPV6-TC</a>,
494      * <a href="http://www.ietf.org/rfc/rfc3416.txt">SNMPv2-PDU</a>,
495      * <a href="http://www.ietf.org/rfc/rfc4001.txt">INET-ADDRESS-MIB</a>.
496      *
497      * @return The IP Address representation.
498      * @see #toString
499      * @see #getIpAddress
500      * @see SnmpConstants#ASN_OCTET_STR
501      * @see SnmpConstants#IPADDRESS
502      */
503     /*
504      * TODO: use Java's java.net.InetAddress, so it can be used for IPv4, and IPv6.
505      */
506     public String toIpAddress() {
507         /*
508          * TODO: Does this work? Yes, but will not compile in jdk 1.2.X, and
509          * in order to load (a part of) the stack in Oracle, I need
510          * 1.2.X
511          */
512         /*
513          * String str = "";
514          * try
515          * {
516          * InetAddress iad = InetAddress.getByAddress(value);
517          * str = iad.getHostAddress();
518          * }
519          * catch (java.net.UnknownHostException exc) { }
520          */
521 
522         /*
523         */
524         StringBuffer sb = new StringBuffer(39);
525         int length;
526         long val;
527         length = value.length;
528         if (length > 0) {
529             if (length > 4) {
530                 // IPv6
531                 // Nicked this code from Inet6Address.numericToTextFormat
532                 for (int i = 0; i < length / 2; i++) {
533                     sb.append(Integer.toHexString(((value[i << 1] << 8) & 0xff00)
534                             | (value[(i << 1) + 1] & 0xff)));
535                     if (i < ((length / 2) - 1)) {
536                         sb.append(":");
537                     }
538                 }
539             } else {
540                 // IPv4
541                 for (int i = 0; i < length - 1; i++) {
542                     val = getPositiveValue(i);
543                     sb.append(String.valueOf(val)).append(".");
544                 }
545                 val = getPositiveValue(length - 1);
546                 sb.append(String.valueOf(val));
547             }
548         }
549         return sb.toString();
550     }
551 
552     /**
553      * Returns this Octet as an IP Address.
554      *
555      * Note, the SNMP representation of IPv4 and IPv6 is different:
556      * <ul>
557      * <li>IPv4: IPADDRESS (or ASN_OCTET_STR, see rfc 4001)</li>
558      * <li>IPv6: ASN_OCTET_STR</li>
559      * </ul>
560      * See also
561      * <a href="http://www.ietf.org/rfc/rfc2465.txt">IPV6-TC</a>,
562      * <a href="http://www.ietf.org/rfc/rfc3416.txt">SNMPv2-PDU</a>,
563      * <a href="http://www.ietf.org/rfc/rfc4001.txt">INET-ADDRESS-MIB</a>.
564      *
565      * @return The IP Address representation.
566      * @exception RuntimeException Thrown when the Octets does
567      *                             not represent an InetAddress or when the method
568      *                             internally throws
569      *                             an java.net.UnknownHostException
570      *
571      * @see #toString
572      * @see #toIpAddress
573      * @see SnmpConstants#ASN_OCTET_STR
574      * @see SnmpConstants#IPADDRESS
575      * @since 4_14
576      */
577     public InetAddress getIpAddress()
578             throws RuntimeException {
579         InetAddress iad = null;
580         try {
581             iad = InetAddress.getByAddress(value);
582         } catch (java.net.UnknownHostException exc) {
583             throw new RuntimeException(exc);
584         }
585         return iad;
586     }
587 
588     /**
589      * Returns the positive long for an octet. Only if type is IPADDRESS
590      * can the value be negative anyway.
591      */
592     private long getPositiveValue(int index) {
593         long val = (long) value[index];
594         if (val < 0) {
595             val += 256;
596         }
597         return val;
598     }
599 
600     /**
601      * Returns this Octet as an hexadecimal String, without any prefix.
602      *
603      * @return The hex representation.
604      * @see #toString
605      */
606     public String toHex() {
607         int length;
608         StringBuffer buffer = new StringBuffer("");
609 
610         length = value.length;
611         if (length > 0) {
612             for (int i = 0; i < length - 1; i++) {
613                 buffer.append(SnmpUtilities.toHex(value[i])).append(":");
614             }
615             buffer.append(SnmpUtilities.toHex(value[length - 1]));
616         }
617 
618         return buffer.toString();
619     }
620 
621     /**
622      * Returns this Octet as a display string (text convension). In contrast to the
623      * method toString(), this method does not try to guess whether or
624      * not this string is printable, it just converts it to a
625      * String, using "US-ASCII" character set.
626      *
627      * <p>
628      * DisplayString
629      * represents textual information taken from the NVT
630      * ASCII character set, as defined in pages 4, 10-11
631      * of <a href="http://www.ietf.org/rfc/rfc854.txt">RFC 854</a>.
632      * Any object defined using this syntax
633      * may not exceed 255 characters in length.
634      * Basicly it is US-ASCII with some changes.
635      * </p>
636      *
637      * @return The string representation.
638      * @see #toString
639      */
640     public String toDisplayString() {
641         String str = "";
642         int length;
643 
644         length = value.length;
645         if (length > 0) {
646             try {
647                 str = new String(value, "US-ASCII");
648             } catch (UnsupportedEncodingException exc) {
649                 str = new String(value);
650             }
651             str = str.trim();
652         }
653 
654         return str;
655     }
656 
657     /**
658      * Returns this Octet as an international display string (text
659      * convension).
660      * It calls AsnOctetsPrintableFace.toInternationalDisplayString().
661      *
662      * See
663      * <a href="http://www.ietf.org/rfc/rfc2790.txt">HOST-RESOURCES-MIB</a>
664      * 
665      * @see AsnOctetsPrintableFace#toInternationalDisplayString
666      * @since 4_14
667      */
668     public String toInternationalDisplayString() {
669         return toInternationalDisplayString(printableObject);
670     }
671 
672     /**
673      * As toInternationalDisplayString(), but this methods will use this
674      * specific, one-off AsnOctetsPrintableFace object.
675      * 
676      * @see #toInternationalDisplayString
677      * @since 4_14
678      */
679     public String toInternationalDisplayString(AsnOctetsPrintableFace face) {
680         return face.toInternationalDisplayString(value);
681     }
682 
683     /**
684      * Returns the String representation according to the DateAndTime
685      * convension.
686      * This string it returns is not exactly the same as the
687      * DISPLAY-HINT indicates.
688      * See
689      * <a href="http://www.ietf.org/rfc/rfc2579.txt">SNMPv2-TC</a>
690      *
691      * @since 4_14
692      * @exception RuntimeException Thrown when the number of
693      *                             Octets does not represent the DateAndTime length.
694      * @see #CALFORMAT
695      */
696     public String toCalendar()
697             throws RuntimeException {
698         Calendar cal = this.getCalendar();
699         Date date = cal.getTime();
700         return CALFORMAT.format(date);
701     }
702 
703     /**
704      * Returns the Octets as Calendar according to the DateAndTime text
705      * convension. You can only call this method if
706      * the syntax of this Octet is the DateAndTime text convension.
707      *
708      * @exception RuntimeException Thrown when the number of
709      *                             Octets does not represent the DateAndTime length.
710      *
711      * @since 4_14
712      * @see #AsnOctets(Calendar)
713      */
714     public Calendar getCalendar()
715             throws RuntimeException {
716         Calendar cal = Calendar.getInstance();
717         if (value.length == 8 || value.length == 11) {
718             int year = (int) ((getPositiveValue(0) * 256) + getPositiveValue(1));
719             // Calendar: 0=January
720             int month = value[2] - 1;
721             int day = value[3];
722             int hour = value[4];
723             int min = value[5];
724             int sec = value[6];
725             int msec = value[7] * 100;
726             cal.set(year, month, day, hour, min, sec);
727             cal.set(Calendar.MILLISECOND, msec);
728 
729             if (value.length == 11) {
730                 char dir = (char) value[8];
731                 int hourUTC = value[9];
732                 int minUTC = value[10];
733                 int secUTC = (hourUTC * 60) * 60;
734 
735                 int msecGMT = secUTC * 1000;
736                 if (dir == '-') {
737                     msecGMT = msecGMT * -1;
738                 }
739 
740                 cal.set(Calendar.ZONE_OFFSET, msecGMT);
741             }
742         } else {
743             throw new RuntimeException("AsnOctets is not DateAndTime");
744         }
745         return cal;
746     }
747 
748     /**
749      * Converts this Octet to its corresponding sub-identifiers.
750      * Each octet will be encoded in a separate sub-identifier, by
751      * converting the octet into a positive long.
752      * 
753      * <p>
754      * Use this method when building an OID when this Octet specifies a
755      * conceptual row. For example ipNetToMediaEntry, see
756      * <a href="http://www.ietf.org/rfc/rfc2011.txt">IP-MIB</a>
757      * or SnmpCommunityEntry, see
758      * <a href="http://www.ietf.org/rfc/rfc3584.txt">SNMP-COMMUNITY-MIB</a>
759      * </p>
760      *
761      * <p>
762      * The variable <code>length_implied</code> indicates that this MIB variable
763      * is preceded by the IMPLIED keyword:
764      * </p>
765      * <ul>
766      * <li>
767      * The IMPLIED keyword can only be present for an Octet having
768      * a variable-length syntax (e.g., variable-length strings or object
769      * identifier-valued objects).
770      * </li>
771      * <li>
772      * The IMPLIED keyword can only be associated with the last
773      * object in the INDEX clause.
774      * </li>
775      * <li>
776      * The IMPLIED keyword may not be used on a variable-length
777      * string Octet if that string might have a value of zero-length.
778      * </li>
779      * </ul>
780      *
781      * <p>
782      * If the length is implied, no extra sub-identifier will be created to
783      * indicate its length. <br/>
784      * If the length is not implied, the first
785      * sub-identifier will be the length of the Octet.
786      * </p>
787      *
788      * <p>
789      * If this Octet is of type IPADDRESS, length_implied should be false.
790      * </p>
791      *
792      * <p>
793      * The mapping of the INDEX clause is
794      * explained in <a href="http://www.ietf.org/rfc/rfc2578.txt">SNMPv2-SMI</a>,
795      * section 7.7.
796      * </p>
797      *
798      * @param length_implied Indicates if the length of this octet is
799      *                       implied.
800      *
801      * @see AsnObjectId#add(long[])
802      */
803     public long[] toSubOid(boolean length_implied) {
804         long sub_oid[];
805         int index = 0;
806         int length = value.length;
807 
808         if (length_implied) {
809             sub_oid = new long[length];
810         } else {
811             sub_oid = new long[length + 1];
812             sub_oid[0] = length;
813             index++;
814         }
815 
816         for (int i = 0; i < length; i++) {
817             sub_oid[index] = getPositiveValue(i);
818             index++;
819         }
820         return sub_oid;
821     }
822 
823     /**
824      * Compares this Octet to the specified object.
825      * The result is <code>true</code> if and only if the argument is not
826      * <code>null</code> and is an <code>AsnOctets</code> object that represents
827      * the same sequence of octets as this Octet.
828      *
829      * @param anObject the object to compare this <code>AsnOctets</code>
830      *                 against.
831      * @return <code>true</code> if the <code>AsnOctets </code>are equal;
832      *         <code>false</code> otherwise.
833      */
834     public boolean equals(Object anObject) {
835         if (this == anObject) {
836             return true;
837         }
838         if (anObject instanceof AsnOctets) {
839             AsnOctets anotherOctet = (AsnOctets) anObject;
840             int n = value.length;
841             if (n == anotherOctet.value.length) {
842                 byte v1[] = value;
843                 byte v2[] = anotherOctet.value;
844                 int i = 0;
845                 int j = 0;
846                 while (n-- != 0) {
847                     if (v1[i++] != v2[j++]) {
848                         return false;
849                     }
850                 }
851                 return true;
852             }
853         }
854         return false;
855     }
856 
857     /**
858      * Returns a hash code for this Octet. The hash code for a
859      * <code>AsnOctets</code> object is computed as
860      * <blockquote>
861      * 
862      * <pre>
863      * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
864      * </pre>
865      * 
866      * </blockquote>
867      * using <code>int</code> arithmetic, where <code>s[i]</code> is the
868      * <i>i</i>th character of the Octet, <code>n</code> is the length of
869      * the Octet, and <code>^</code> indicates exponentiation.
870      * (The hash value of the empty Octet is zero.)
871      *
872      * @return a hash code value for this Octet.
873      */
874     public int hashCode() {
875         int h = hash;
876         if (h == 0) {
877             int off = 0;
878             byte val[] = value;
879             int len = value.length;
880 
881             for (int i = 0; i < len; i++) {
882                 h = 31 * h + val[off++];
883             }
884             hash = h;
885         }
886         return h;
887     }
888 
889 }