1 /*
2  * Copyright (c) 2001, 2006, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.jmx.snmp;
27 
28 import com.sun.jmx.snmp.SnmpSecurityParameters;
29 // java imports
30 //
31 import java.util.Vector;
32 import java.net.InetAddress;
33 
34 
35 import com.sun.jmx.snmp.SnmpStatusException;
36 /**
37  * A partially decoded representation of an SNMP packet. It contains
38  * the information contained in any SNMP message (SNMPv1, SNMPv2 or
39  * SNMPv3).
40  * <p><b>This API is a Sun Microsystems internal API  and is subject
41  * to change without notice.</b></p>
42  * @since 1.5
43  */
44 public abstract class SnmpMsg implements SnmpDefinitions {
45     /**
46      * The protocol version.
47      * <P><CODE>decodeMessage</CODE> and <CODE>encodeMessage</CODE> do not
48      * perform any check on this value.
49      * <BR><CODE>decodeSnmpPdu</CODE> and <CODE>encodeSnmpPdu</CODE> only
50      * accept  the values 0 (for SNMPv1), 1 (for SNMPv2) and 3 (for SNMPv3).
51      */
52     public int version = 0;
53 
54     /**
55      * Encoding of the PDU.
56      * <P>This is usually the BER encoding of the PDU's syntax
57      * defined in RFC1157 and RFC1902. However, this can be authenticated
58      * or encrypted data (but you need to implemented your own
59      * <CODE>SnmpPduFactory</CODE> class).
60      */
61     public byte[] data = null;
62 
63     /**
64      * Number of useful bytes in the <CODE>data</CODE> field.
65      */
66     public int dataLength = 0;
67 
68     /**
69      * Source or destination address.
70      * <BR>For an incoming message it's the source.
71      * For an outgoing message it's the destination.
72      */
73     public InetAddress address = null;
74 
75     /**
76      * Source or destination port.
77      * <BR>For an incoming message it's the source.
78      * For an outgoing message it's the destination.
79      */
80     public int port = 0;
81     /**
82      * Security parameters. Contain informations according to Security Model (Usm, community string based, ...).
83      */
84     public SnmpSecurityParameters securityParameters = null;
85     /**
86      * Returns the encoded SNMP version present in the passed byte array.
87      * @param data The unmarshalled SNMP message.
88      * @return The SNMP version (0, 1 or 3).
89      */
getProtocolVersion(byte[] data)90     public static int getProtocolVersion(byte[] data)
91         throws SnmpStatusException {
92         int version = 0;
93         BerDecoder bdec = null;
94         try {
95             bdec = new BerDecoder(data);
96             bdec.openSequence();
97             version = bdec.fetchInteger();
98         }
99         catch(BerException x) {
100             throw new SnmpStatusException("Invalid encoding") ;
101         }
102         try {
103             bdec.closeSequence();
104         }
105         catch(BerException x) {
106         }
107         return version;
108     }
109 
110     /**
111      * Returns the associated request ID.
112      * @param data The flat message.
113      * @return The request ID.
114      */
getRequestId(byte[] data)115     public abstract int getRequestId(byte[] data) throws SnmpStatusException;
116 
117     /**
118      * Encodes this message and puts the result in the specified byte array.
119      * For internal use only.
120      *
121      * @param outputBytes An array to receive the resulting encoding.
122      *
123      * @exception ArrayIndexOutOfBoundsException If the result does not fit
124      *                                           into the specified array.
125      */
encodeMessage(byte[] outputBytes)126     public abstract int encodeMessage(byte[] outputBytes)
127         throws SnmpTooBigException;
128 
129      /**
130      * Decodes the specified bytes and initializes this message.
131      * For internal use only.
132      *
133      * @param inputBytes The bytes to be decoded.
134      *
135      * @exception SnmpStatusException If the specified bytes are not a valid encoding.
136      */
decodeMessage(byte[] inputBytes, int byteCount)137     public abstract void decodeMessage(byte[] inputBytes, int byteCount)
138         throws SnmpStatusException;
139 
140      /**
141      * Initializes this message with the specified <CODE>pdu</CODE>.
142      * <P>
143      * This method initializes the data field with an array of
144      * <CODE>maxDataLength</CODE> bytes. It encodes the <CODE>pdu</CODE>.
145      * The resulting encoding is stored in the data field
146      * and the length of the encoding is stored in <CODE>dataLength</CODE>.
147      * <p>
148      * If the encoding length exceeds <CODE>maxDataLength</CODE>,
149      * the method throws an exception.
150      *
151      * @param pdu The PDU to be encoded.
152      * @param maxDataLength The maximum length permitted for the data field.
153      *
154      * @exception SnmpStatusException If the specified <CODE>pdu</CODE> is not valid.
155      * @exception SnmpTooBigException If the resulting encoding does not fit
156      * into <CODE>maxDataLength</CODE> bytes.
157      * @exception ArrayIndexOutOfBoundsException If the encoding exceeds <CODE>maxDataLength</CODE>.
158      */
encodeSnmpPdu(SnmpPdu pdu, int maxDataLength)159     public abstract void encodeSnmpPdu(SnmpPdu pdu, int maxDataLength)
160         throws SnmpStatusException, SnmpTooBigException;
161 
162 
163     /**
164      * Gets the PDU encoded in this message.
165      * <P>
166      * This method decodes the data field and returns the resulting PDU.
167      *
168      * @return The resulting PDU.
169      * @exception SnmpStatusException If the encoding is not valid.
170      */
decodeSnmpPdu()171     public abstract SnmpPdu decodeSnmpPdu()
172         throws SnmpStatusException;
173 
174     /**
175      * Dumps the content of a byte buffer using hexadecimal form.
176      *
177      * @param b The buffer to dump.
178      * @param offset The position of the first byte to be dumped.
179      * @param len The number of bytes to be dumped starting from offset.
180      *
181      * @return The string containing the dump.
182      */
dumpHexBuffer(byte [] b, int offset, int len)183     public static String dumpHexBuffer(byte [] b, int offset, int len) {
184         StringBuffer buf = new StringBuffer(len << 1) ;
185         int k = 1 ;
186         int flen = offset + len ;
187 
188         for (int i = offset; i < flen ; i++) {
189             int j = b[i] & 0xFF ;
190             buf.append(Character.forDigit((j >>> 4) , 16)) ;
191             buf.append(Character.forDigit((j & 0x0F), 16)) ;
192             k++ ;
193             if (k%16 == 0) {
194                 buf.append('\n') ;
195                 k = 1 ;
196             } else
197                 buf.append(' ') ;
198         }
199         return buf.toString() ;
200     }
201 
202     /**
203      * Dumps this message in a string.
204      *
205      * @return The string containing the dump.
206      */
printMessage()207     public String printMessage() {
208         StringBuffer sb = new StringBuffer() ;
209         sb.append("Version: ") ;
210         sb.append(version) ;
211         sb.append("\n") ;
212         if (data == null) {
213             sb.append("Data: null") ;
214         }
215         else {
216             sb.append("Data: {\n") ;
217             sb.append(dumpHexBuffer(data, 0, dataLength)) ;
218             sb.append("\n}\n") ;
219         }
220 
221         return sb.toString() ;
222     }
223 
224     /**
225      * For SNMP Runtime private use only.
226      */
encodeVarBindList(BerEncoder benc, SnmpVarBind[] varBindList)227     public void encodeVarBindList(BerEncoder benc,
228                                   SnmpVarBind[] varBindList)
229         throws SnmpStatusException, SnmpTooBigException {
230         //
231         // Remember: the encoder does backward encoding
232         //
233         int encodedVarBindCount = 0 ;
234         try {
235             benc.openSequence() ;
236             if (varBindList != null) {
237                 for (int i = varBindList.length - 1 ; i >= 0 ; i--) {
238                     SnmpVarBind bind = varBindList[i] ;
239                     if (bind != null) {
240                         benc.openSequence() ;
241                         encodeVarBindValue(benc, bind.value) ;
242                         benc.putOid(bind.oid.longValue()) ;
243                         benc.closeSequence() ;
244                         encodedVarBindCount++ ;
245                     }
246                 }
247             }
248             benc.closeSequence() ;
249         }
250         catch(ArrayIndexOutOfBoundsException x) {
251             throw new SnmpTooBigException(encodedVarBindCount) ;
252         }
253     }
254 
255     /**
256      * For SNMP Runtime private use only.
257      */
encodeVarBindValue(BerEncoder benc, SnmpValue v)258     void encodeVarBindValue(BerEncoder benc,
259                             SnmpValue v)throws SnmpStatusException {
260         if (v == null) {
261             benc.putNull() ;
262         }
263         else if (v instanceof SnmpIpAddress) {
264             benc.putOctetString(((SnmpIpAddress)v).byteValue(), SnmpValue.IpAddressTag) ;
265         }
266         else if (v instanceof SnmpCounter) {
267             benc.putInteger(((SnmpCounter)v).longValue(), SnmpValue.CounterTag) ;
268         }
269         else if (v instanceof SnmpGauge) {
270             benc.putInteger(((SnmpGauge)v).longValue(), SnmpValue.GaugeTag) ;
271         }
272         else if (v instanceof SnmpTimeticks) {
273             benc.putInteger(((SnmpTimeticks)v).longValue(), SnmpValue.TimeticksTag) ;
274         }
275         else if (v instanceof SnmpOpaque) {
276             benc.putOctetString(((SnmpOpaque)v).byteValue(), SnmpValue.OpaqueTag) ;
277         }
278         else if (v instanceof SnmpInt) {
279             benc.putInteger(((SnmpInt)v).intValue()) ;
280         }
281         else if (v instanceof SnmpString) {
282             benc.putOctetString(((SnmpString)v).byteValue()) ;
283         }
284         else if (v instanceof SnmpOid) {
285             benc.putOid(((SnmpOid)v).longValue()) ;
286         }
287         else if (v instanceof SnmpCounter64) {
288             if (version == snmpVersionOne) {
289                 throw new SnmpStatusException("Invalid value for SNMP v1 : " + v) ;
290             }
291             benc.putInteger(((SnmpCounter64)v).longValue(), SnmpValue.Counter64Tag) ;
292         }
293         else if (v instanceof SnmpNull) {
294             int tag = ((SnmpNull)v).getTag() ;
295             if ((version == snmpVersionOne) && (tag != SnmpValue.NullTag)) {
296                 throw new SnmpStatusException("Invalid value for SNMP v1 : " + v) ;
297             }
298             if ((version == snmpVersionTwo) &&
299                 (tag != SnmpValue.NullTag) &&
300                 (tag != SnmpVarBind.errNoSuchObjectTag) &&
301                 (tag != SnmpVarBind.errNoSuchInstanceTag) &&
302                 (tag != SnmpVarBind.errEndOfMibViewTag)) {
303                 throw new SnmpStatusException("Invalid value " + v) ;
304             }
305             benc.putNull(tag) ;
306         }
307         else {
308             throw new SnmpStatusException("Invalid value " + v) ;
309         }
310 
311     }
312 
313 
314     /**
315      * For SNMP Runtime private use only.
316      */
decodeVarBindList(BerDecoder bdec)317     public SnmpVarBind[] decodeVarBindList(BerDecoder bdec)
318         throws BerException {
319             bdec.openSequence() ;
320             Vector<SnmpVarBind> tmp = new Vector<SnmpVarBind>() ;
321             while (bdec.cannotCloseSequence()) {
322                 SnmpVarBind bind = new SnmpVarBind() ;
323                 bdec.openSequence() ;
324                 bind.oid = new SnmpOid(bdec.fetchOid()) ;
325                 bind.setSnmpValue(decodeVarBindValue(bdec)) ;
326                 bdec.closeSequence() ;
327                 tmp.addElement(bind) ;
328             }
329             bdec.closeSequence() ;
330             SnmpVarBind[] varBindList= new SnmpVarBind[tmp.size()] ;
331             tmp.copyInto(varBindList);
332             return varBindList ;
333         }
334 
335 
336     /**
337      * For SNMP Runtime private use only.
338      */
decodeVarBindValue(BerDecoder bdec)339     SnmpValue decodeVarBindValue(BerDecoder bdec)
340         throws BerException {
341         SnmpValue result = null ;
342         int tag = bdec.getTag() ;
343 
344         // bugId 4641696 : RuntimeExceptions must be transformed in
345         //                 BerException.
346         switch(tag) {
347 
348             //
349             // Simple syntax
350             //
351         case BerDecoder.IntegerTag :
352             try {
353                 result = new SnmpInt(bdec.fetchInteger()) ;
354             } catch(RuntimeException r) {
355                 throw new BerException();
356                 // BerException("Can't build SnmpInt from decoded value.");
357             }
358             break ;
359         case BerDecoder.OctetStringTag :
360             try {
361                 result = new SnmpString(bdec.fetchOctetString()) ;
362             } catch(RuntimeException r) {
363                 throw new BerException();
364                 // BerException("Can't build SnmpString from decoded value.");
365             }
366             break ;
367         case BerDecoder.OidTag :
368             try {
369                 result = new SnmpOid(bdec.fetchOid()) ;
370             } catch(RuntimeException r) {
371                 throw new BerException();
372                 // BerException("Can't build SnmpOid from decoded value.");
373             }
374             break ;
375         case BerDecoder.NullTag :
376             bdec.fetchNull() ;
377             try {
378                 result = new SnmpNull() ;
379             } catch(RuntimeException r) {
380                 throw new BerException();
381                 // BerException("Can't build SnmpNull from decoded value.");
382             }
383             break ;
384 
385             //
386             // Application syntax
387             //
388         case SnmpValue.IpAddressTag :
389             try {
390                 result = new SnmpIpAddress(bdec.fetchOctetString(tag)) ;
391             } catch (RuntimeException r) {
392                 throw new  BerException();
393               // BerException("Can't build SnmpIpAddress from decoded value.");
394             }
395             break ;
396         case SnmpValue.CounterTag :
397             try {
398                 result = new SnmpCounter(bdec.fetchIntegerAsLong(tag)) ;
399             } catch(RuntimeException r) {
400                 throw new BerException();
401                 // BerException("Can't build SnmpCounter from decoded value.");
402             }
403             break ;
404         case SnmpValue.GaugeTag :
405             try {
406                 result = new SnmpGauge(bdec.fetchIntegerAsLong(tag)) ;
407             } catch(RuntimeException r) {
408                 throw new BerException();
409                 // BerException("Can't build SnmpGauge from decoded value.");
410             }
411             break ;
412         case SnmpValue.TimeticksTag :
413             try {
414                 result = new SnmpTimeticks(bdec.fetchIntegerAsLong(tag)) ;
415             } catch(RuntimeException r) {
416                 throw new BerException();
417              // BerException("Can't build SnmpTimeticks from decoded value.");
418             }
419             break ;
420         case SnmpValue.OpaqueTag :
421             try {
422                 result = new SnmpOpaque(bdec.fetchOctetString(tag)) ;
423             } catch(RuntimeException r) {
424                 throw new BerException();
425                 // BerException("Can't build SnmpOpaque from decoded value.");
426             }
427             break ;
428 
429             //
430             // V2 syntaxes
431             //
432         case SnmpValue.Counter64Tag :
433             if (version == snmpVersionOne) {
434                 throw new BerException(BerException.BAD_VERSION) ;
435             }
436             try {
437                 result = new SnmpCounter64(bdec.fetchIntegerAsLong(tag)) ;
438             } catch(RuntimeException r) {
439                 throw new BerException();
440              // BerException("Can't build SnmpCounter64 from decoded value.");
441             }
442             break ;
443 
444         case SnmpVarBind.errNoSuchObjectTag :
445             if (version == snmpVersionOne) {
446                 throw new BerException(BerException.BAD_VERSION) ;
447             }
448             bdec.fetchNull(tag) ;
449             result = SnmpVarBind.noSuchObject ;
450             break ;
451 
452         case SnmpVarBind.errNoSuchInstanceTag :
453             if (version == snmpVersionOne) {
454                 throw new BerException(BerException.BAD_VERSION) ;
455             }
456             bdec.fetchNull(tag) ;
457             result = SnmpVarBind.noSuchInstance ;
458             break ;
459 
460         case SnmpVarBind.errEndOfMibViewTag :
461             if (version == snmpVersionOne) {
462                 throw new BerException(BerException.BAD_VERSION) ;
463             }
464             bdec.fetchNull(tag) ;
465             result = SnmpVarBind.endOfMibView ;
466             break ;
467 
468         default:
469             throw new BerException() ;
470 
471         }
472 
473         return result ;
474     }
475 
476 }
477