KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > snmp4j > mp > MPv3


1 /*_############################################################################
2   _##
3   _## SNMP4J - MPv3.java
4   _##
5   _## Copyright 2003-2007 Frank Fock and Jochen Katz (SNMP4J.org)
6   _##
7   _## Licensed under the Apache License, Version 2.0 (the "License");
8   _## you may not use this file except in compliance with the License.
9   _## You may obtain a copy of the License at
10   _##
11   _## http://www.apache.org/licenses/LICENSE-2.0
12   _##
13   _## Unless required by applicable law or agreed to in writing, software
14   _## distributed under the License is distributed on an "AS IS" BASIS,
15   _## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   _## See the License for the specific language governing permissions and
17   _## limitations under the License.
18   _##
19   _##########################################################################*/

20
21 package org.snmp4j.mp;
22
23 import java.io.*;
24 import java.net.*;
25 import java.nio.ByteBuffer JavaDoc;
26 import java.util.*;
27
28 import org.snmp4j.*;
29 import org.snmp4j.asn1.*;
30 import org.snmp4j.event.*;
31 import org.snmp4j.log.*;
32 import org.snmp4j.security.*;
33 import org.snmp4j.smi.*;
34
35 /**
36  * The <code>MPv3</code> is the message processing model for SNMPv3.
37  *
38  * @author Frank Fock
39  * @version 1.8.2
40  */

41 public class MPv3
42     implements MessageProcessingModel {
43
44   public static final int ID = MessageProcessingModel.MPv3;
45   public static final int MPv3_REPORTABLE_FLAG = 4;
46   public static final int MAX_MESSAGE_ID = 2147483647;
47
48   public static final int MAXLEN_ENGINE_ID = 32;
49   public static final int MINLEN_ENGINE_ID = 5;
50
51   private static final int MAX_HEADER_PAYLOAD_LENGTH =
52       // length of msgFlags
53
new OctetString("\0").getBERLength() +
54       // length of msgID, msgMaxSize, securityModel
55
3 * new Integer32(Integer.MAX_VALUE).getBERLength();
56
57   private static final int MAX_HEADER_LENGTH =
58       MAX_HEADER_PAYLOAD_LENGTH +
59       BER.getBERLengthOfLength(MAX_HEADER_PAYLOAD_LENGTH) + 1;
60
61   private SecurityProtocols securityProtocols;
62
63   private static final LogAdapter logger = LogFactory.getLogger(MPv3.class);
64   private SecurityModels securityModels;
65
66   private Cache cache;
67   private Hashtable engineIDs;
68   private byte[] localEngineID;
69
70   private int currentMsgID = new Random().nextInt(MAX_MESSAGE_ID);
71
72   // Enterprise ID of AGENT++
73
private static int enterpriseID = 4976;
74
75   private CounterSupport counterSupport;
76
77   transient Vector snmpEngineListeners;
78
79
80   /**
81    * Creates a MPv3 with a default local engine ID.
82    */

83   public MPv3() {
84     engineIDs = new Hashtable();
85     cache = new Cache();
86     securityProtocols = SecurityProtocols.getInstance();
87     securityModels = SecurityModels.getInstance();
88     localEngineID = createLocalEngineID();
89     counterSupport = CounterSupport.getInstance();
90   }
91
92   /**
93    * Creates a MPv3 with a supplied local engine ID.
94    * @param localEngineID
95    * the local engine ID. Its length must be >= 5 and <= 32.
96    */

97   public MPv3(byte[] localEngineID) {
98     this();
99     setLocalEngineID(localEngineID);
100   }
101
102   /**
103    * Creates a local engine ID based on the local IP address.
104    * @return
105    * a new local engine ID.
106    */

107   public static byte[] createLocalEngineID() {
108     byte[] engineID = new byte[5];
109     engineID[0] = (byte)(0x80 | ((enterpriseID >> 24) & 0xFF));
110     engineID[1] = (byte)((enterpriseID >> 16) & 0xFF);
111     engineID[2] = (byte)((enterpriseID >> 8) & 0xFF);
112     engineID[3] = (byte)(enterpriseID & 0xFF);
113     engineID[4] = 1;
114     OctetString os = new OctetString();
115     try {
116       os.setValue(InetAddress.getLocalHost().getAddress());
117     }
118     catch (UnknownHostException ex) {
119       logger.debug("Local host cannot be determined for creation of local engine ID");
120       engineID[4] = 4;
121       os.setValue("SNMP4J".getBytes());
122     }
123     OctetString ownEngineID = new OctetString(engineID);
124     ownEngineID.append(os);
125     return ownEngineID.getValue();
126   }
127
128   /**
129    * Creates a local engine ID based on the ID string supplied
130    * @param id
131    * an ID string.
132    * @return
133    * a new local engine ID.
134    */

135   public static byte[] createLocalEngineID(OctetString id) {
136     byte[] engineID = new byte[5];
137     engineID[0] = (byte)(0x80 | ((enterpriseID >> 24) & 0xFF));
138     engineID[1] = (byte)((enterpriseID >> 16) & 0xFF);
139     engineID[2] = (byte)((enterpriseID >> 8) & 0xFF);
140     engineID[3] = (byte)(enterpriseID & 0xFF);
141     engineID[4] = 4;
142     OctetString ownEngineID = new OctetString(engineID);
143     ownEngineID.append(id);
144     return ownEngineID.getValue();
145   }
146
147   /**
148    * Sets the local engine ID. This value must not be changed after message
149    * processing has been started.
150    * @param engineID
151    * the local engine ID. Its length must be >= 5 and <= 32.
152    */

153   public void setLocalEngineID(byte[] engineID) {
154     if ((engineID == null) ||
155         (engineID.length < MINLEN_ENGINE_ID) ||
156         (engineID.length > MAXLEN_ENGINE_ID)) {
157       throw new IllegalArgumentException JavaDoc("Illegal (local) engine ID");
158     }
159     this.localEngineID = engineID;
160   }
161
162   /**
163    * Gets a copy of the local engine ID.
164    * @return
165    * a byte array containing the local engine ID.
166    */

167   public byte[] getLocalEngineID() {
168     byte[] retval = new byte[localEngineID.length];
169     System.arraycopy(localEngineID, 0, retval, 0, localEngineID.length);
170     return retval;
171   }
172
173   /**
174    * Creates and initializes the default security protocols.
175    * @see SecurityProtocols#addDefaultProtocols()
176    */

177   public void initDefaults() {
178     securityProtocols.addDefaultProtocols();
179   }
180
181   /**
182    * Gets an authentication protocol for the supplied ID.
183    * @param id
184    * an authentication protocol OID.
185    * @return
186    * an <code>AuthenticationProtocol</code> instance if the supplied ID
187    * is supported, otherwise <code>null</code> is returned.
188    */

189   public AuthenticationProtocol getAuthProtocol(OID id) {
190     return securityProtocols.getAuthenticationProtocol(id);
191   }
192
193   /**
194    * Gets an privacy protocol for the supplied ID.
195    * @param id
196    * an privacy protocol OID.
197    * @return
198    * an <code>PrivacyProtocol</code> instance if the supplied ID
199    * is supported, otherwise <code>null</code> is returned.
200    */

201   public PrivacyProtocol getPrivProtocol(OID id) {
202     return securityProtocols.getPrivacyProtocol(id);
203   }
204
205   /**
206    * Gets the security model for the supplied ID.
207    * @param id
208    * a security model ID.
209    * @return
210    * a <code>SecurityModel</code> instance if the supplied ID
211    * is supported, otherwise <code>null</code> is returned.
212    */

213   public SecurityModel getSecurityModel(int id) {
214     return securityModels.getSecurityModel(new Integer32(id));
215   }
216
217   public int getID() {
218     return ID;
219   }
220
221   public boolean isProtocolVersionSupported(int version) {
222     return (version == SnmpConstants.version3);
223   }
224
225   /**
226    * Adds an engine ID (other than the local engine ID) to the internal storage.
227    * @param address
228    * the <code>Address</code> of the remote SNMP engine.
229    * @param engineID
230    * the engine ID of the remote SNMP engine.
231    * @return
232    * <code>true</code> if the engine ID has been added, <code>false</code>
233    * otherwise (if the supplied <code>engineID</code> equals the local one).
234    */

235   public boolean addEngineID(Address address, OctetString engineID) {
236     if (!Arrays.equals(this.localEngineID, engineID.getValue())) {
237       engineIDs.put(address, engineID);
238       if (snmpEngineListeners != null) {
239         fireEngineChanged(new SnmpEngineEvent(this,
240                                               SnmpEngineEvent.ADDED_ENGINE_ID,
241                                               engineID, address));
242       }
243       return true;
244     }
245     return false;
246   }
247
248   /**
249    * Gets the engine ID associated with the supplied address from the local
250    * storage and fires the corresponding {@link SnmpEngineEvent}.
251    * @param address
252    * the <code>Address</code> of the remote SNMP engine.
253    * @return
254    * the engine ID of the remote SNMP engine or <code>null</code> if there
255    * is no entry for <code>address</code> in the local storage.
256    */

257   public OctetString getEngineID(Address address) {
258     return (OctetString) engineIDs.get(address);
259   }
260
261   /**
262    * Removes an engine ID association from the local storage and fires the
263    * corresponding {@link SnmpEngineEvent}.
264    * @param address
265    * the <code>Address</code> of the remote SNMP engine for whose engine ID
266    * is to be removed.
267    * @return
268    * the removed engine ID of the remote SNMP engine or <code>null</code> if
269    * there is no entry for <code>address</code> in the local storage.
270    */

271   public OctetString removeEngineID(Address address) {
272     OctetString engineID = (OctetString) engineIDs.remove(address);
273     if ((engineID != null) && (snmpEngineListeners != null)) {
274       fireEngineChanged(new SnmpEngineEvent(this,
275                                             SnmpEngineEvent.REMOVED_ENGINE_ID,
276                                             engineID, address));
277     }
278     return engineID;
279   }
280
281
282   /**
283    * The <code>CacheEntry</code> class holds state reference information
284    * for the MPv3 message processing model for a single message.
285    * @author Frank Fock
286    * @version 1.0
287    */

288   protected static class CacheEntry extends StateReference {
289     int msgID;
290     long transactionID;
291     byte[] secEngineID;
292     SecurityModel secModel;
293     byte[] secName;
294     int secLevel;
295     byte[] contextEngineID;
296     byte[] contextName;
297     SecurityStateReference secStateReference;
298     int errorCode;
299
300     public CacheEntry(int msgID,
301                       long reqID,
302                       byte[] secEngineID,
303                       SecurityModel secModel,
304                       byte[] secName,
305                       int secLevel,
306                       byte[] contextEngineID,
307                       byte[] contextName,
308                       SecurityStateReference secStateReference,
309                       int errorCode) {
310       this.msgID = msgID;
311       this.transactionID = reqID;
312       this.secEngineID = secEngineID;
313       this.secModel = secModel;
314       this.secName = secName;
315       this.secLevel = secLevel;
316       this.contextEngineID = contextEngineID;
317       this.contextName = contextName;
318       this.secStateReference = secStateReference;
319       this.errorCode = errorCode;
320     }
321   }
322
323   /**
324    * The <code>Cache</code> stores state reference information for the MPv3.
325    * @author Frank Fock
326    * @version 1.0
327    */

328   protected static class Cache {
329
330     private Map entries = new WeakHashMap(25);
331
332     /**
333      * Adds a <code>StateReference</code> to the cache.
334      * The <code>PduHandle</code> of the supplied entry will be set to
335      * <code>null</code> while the entry is part of the cache, because the
336      * cache uses a <code>WeakHashMap</code> internally which uses the
337      * <code>PduHandle</code> as key. When
338      * @param entry
339      * the state reference to add.
340      * @return
341      * {@link SnmpConstants#SNMP_MP_DOUBLED_MESSAGE} if the entry already
342      * exists and {@link SnmpConstants#SNMP_MP_OK} on success.
343      */

344     public synchronized int addEntry(StateReference entry) {
345       if (logger.isDebugEnabled()) {
346         logger.debug("Adding cache entry: "+entry);
347       }
348       StateReference existing =
349           (StateReference) entries.get(entry.getPduHandle());
350       if (existing != null) {
351         if (existing.equals(entry)) {
352           if (logger.isDebugEnabled()) {
353             logger.debug("Doubled message: "+entry);
354           }
355           return SnmpConstants.SNMP_MP_DOUBLED_MESSAGE;
356         }
357       }
358       // add it
359
PduHandle key = entry.getPduHandle();
360       // because we are using a weak has map for the cache, we need to null out
361
// our key from the entry.
362
entry.setPduHandle(null);
363       entries.put(key, entry);
364       return SnmpConstants.SNMP_MP_OK;
365     }
366
367     /**
368      * Get the first cache entry with specified message ID.
369      * @param msgID
370      * a message ID.
371      * @return
372      * a <code>StateReference</code> instance with the given message ID or
373      * <code>null</code> if such an entry cannot be found.
374      */

375 /*
376     public StateReference getEntry(int msgID) {
377       for (Iterator it = entries.iterator(); it.hasNext(); ) {
378         StateReference e = (StateReference) it.next();
379         if (e.getMsgID() == msgID) {
380           return e;
381         }
382       }
383       return null;
384     }
385 */

386     /**
387      * Delete the cache entry with the supplied <code>PduHandle</code>.
388      * @param pduHandle
389      * a pduHandle.
390      * @return
391      * <code>true</code> if an entry has been deleted, <code>false</code>
392      * otherwise.
393      */

394     public synchronized boolean deleteEntry(PduHandle pduHandle) {
395       StateReference e = (StateReference) entries.remove(pduHandle);
396       return (e != null);
397     }
398
399     /**
400      * Pop the cache entry with the supplied ID from the cache.
401      * @param msgID
402      * a message ID.
403      * @return
404      * a <code>CacheEntry</code> instance with the given message ID or
405      * <code>null</code> if such an entry cannot be found. If a cache entry
406      * is returned, the same is removed from the cache.
407      */

408     public synchronized StateReference popEntry(int msgID) {
409       for (Iterator it = entries.keySet().iterator(); it.hasNext(); ) {
410         PduHandle key = (PduHandle) it.next();
411         StateReference e = (StateReference) entries.get(key);
412         if ((e != null) && (e.getMsgID() == msgID)) {
413           it.remove();
414           e.setPduHandle(key);
415           if (logger.isDebugEnabled()) {
416             logger.debug("Removed cache entry: "+e);
417           }
418           return e;
419         }
420       }
421       return null;
422     }
423   }
424
425   /**
426    * The <code>HeaderData</code> represents the message header information
427    * of SNMPv3 message.
428    * @author Frank Fock
429    * @version 1.0
430    */

431   protected static class HeaderData
432       implements BERSerializable {
433
434     public static final byte FLAG_AUTH = 0x01;
435     public static final byte FLAG_PRIV = 0x02;
436
437     Integer32 msgID = new Integer32(0);
438     Integer32 msgMaxSize = new Integer32(Integer.MAX_VALUE);
439     OctetString msgFlags = new OctetString(new byte[1]);
440     Integer32 securityModel = new Integer32(SecurityModel.SECURITY_MODEL_ANY);
441
442     public void setMsgID(int msgID) {
443       this.msgID.setValue(msgID);
444     }
445
446     public int getMsgID() {
447       return msgID.getValue();
448     }
449
450     public void setMsgMaxSize(int msgMaxSize) {
451       this.msgMaxSize.setValue(msgMaxSize);
452     }
453
454     public int getMsgMaxSize() {
455       return msgMaxSize.getValue();
456     }
457
458     public void setMsgFlags(int flags) {
459       this.msgFlags.getValue()[0] = (byte) flags;
460     }
461
462     public int getMsgFlags() {
463       return msgFlags.getValue()[0] & 0xFF;
464     }
465
466     public void setSecurityModel(int model) {
467       securityModel.setValue(model);
468     }
469
470     public int getSecurityModel() {
471       return securityModel.getValue();
472     }
473
474     public int getBERPayloadLength() {
475       int length = msgID.getBERLength();
476       length += msgMaxSize.getBERLength();
477       length += msgFlags.getBERLength();
478       length += securityModel.getBERLength();
479       return length;
480     }
481
482     public int getBERLength() {
483       int length = getBERPayloadLength();
484       length += BER.getBERLengthOfLength(length) + 1;
485       return length;
486     }
487
488     public void decodeBER(BERInputStream message) throws IOException {
489       BER.MutableByte type = new BER.MutableByte();
490       int length = BER.decodeHeader(message, type);
491       if (type.getValue() != BER.SEQUENCE) {
492         throw new IOException("Unexpected sequence header type: " +
493                               type.getValue());
494       }
495       msgID.decodeBER(message);
496       msgMaxSize.decodeBER(message);
497       if (msgMaxSize.getValue() < 484) {
498         throw new IOException("Invalid msgMaxSize: " + msgMaxSize);
499       }
500       msgFlags.decodeBER(message);
501       if (msgFlags.length() != 1) {
502         throw new IOException("Message flags length != 1: " + msgFlags.length());
503       }
504       securityModel.decodeBER(message);
505       if (logger.isDebugEnabled()) {
506         logger.debug("SNMPv3 header decoded: msgId=" + msgID +
507                      ", msgMaxSize=" + msgMaxSize +
508                      ", msgFlags=" + msgFlags.toHexString() +
509                      ", secModel=" + securityModel);
510       }
511       BER.checkSequenceLength(length, this);
512     }
513
514     public void encodeBER(OutputStream outputStream) throws IOException {
515       BER.encodeHeader(outputStream, BER.SEQUENCE, getBERPayloadLength());
516       msgID.encodeBER(outputStream);
517       msgMaxSize.encodeBER(outputStream);
518       msgFlags.encodeBER(outputStream);
519       securityModel.encodeBER(outputStream);
520     }
521   }
522
523   /**
524    * Gets unique message ID.
525    * @return
526    * a message ID >= 1 and <= {@link #MAX_MESSAGE_ID}.
527    */

528   public synchronized int getNextMessageID() {
529     if (currentMsgID >= MAX_MESSAGE_ID) {
530       currentMsgID = 1;
531     }
532     return currentMsgID++;
533   }
534
535   /**
536    * Gets the security protocols supported by this <code>MPv3</code>.
537    * @return
538    * return a <code>SecurityProtocols</code>.
539    */

540   public SecurityProtocols getSecurityProtocols() {
541     return securityProtocols;
542   }
543
544   /**
545    * Sets the security protocols for this <code>MPv3</code>.
546    * @param securityProtocols SecurityProtocols
547    */

548   public void setSecurityProtocols(SecurityProtocols securityProtocols) {
549     this.securityProtocols = securityProtocols;
550   }
551
552   /**
553    * Gets the default security model to be used for engine ID discovery.
554    * @return
555    * a security model ID.
556    * @see USM#getID()
557    */

558   protected int getDefaultSecurityModel() {
559     return SecurityModel.SECURITY_MODEL_USM;
560   }
561
562   public void releaseStateReference(PduHandle pduHandle) {
563     cache.deleteEntry(pduHandle);
564   }
565
566   public int prepareOutgoingMessage(Address transportAddress,
567                                     int maxMessageSize,
568                                     int messageProcessingModel,
569                                     int securityModel,
570                                     byte[] securityName,
571                                     int securityLevel,
572                                     PDU pdu,
573                                     boolean expectResponse,
574                                     PduHandle sendPduHandle,
575                                     Address destTransportAddress,
576                                     BEROutputStream outgoingMessage) throws
577       IOException {
578     if (!(pdu instanceof ScopedPDU)) {
579       throw new IllegalArgumentException JavaDoc(
580           "MPv3 only accepts ScopedPDU instances as pdu parameter");
581     }
582     ScopedPDU scopedPDU = (ScopedPDU) pdu;
583     // lookup engine ID
584
byte[] secEngineID = null;
585     OctetString securityEngineID =
586         (OctetString) engineIDs.get(transportAddress);
587     if (securityEngineID != null) {
588       secEngineID = securityEngineID.getValue();
589       if (scopedPDU.getContextEngineID().length() == 0) {
590         if (logger.isDebugEnabled()) {
591           logger.debug("Context engine ID of scoped PDU is empty! Setting it to authoritative engine ID: "+
592                        securityEngineID.toHexString());
593         }
594         scopedPDU.setContextEngineID(new OctetString(secEngineID));
595       }
596     }
597     else {
598       secEngineID = new byte[0];
599     }
600
601     // determine request type
602
if (pdu.isConfirmedPdu()) {
603       if (secEngineID.length == 0) {
604         securityLevel = SecurityLevel.NOAUTH_NOPRIV;
605         securityModel = getDefaultSecurityModel();
606         // do not send any management information
607
scopedPDU = (ScopedPDU) scopedPDU.clone();
608         scopedPDU.clear();
609       }
610     }
611     else {
612       if (scopedPDU.getContextEngineID().length() == 0) {
613         if (logger.isDebugEnabled()) {
614           logger.debug("Context engine ID of unconfirmed scoped PDU is empty! "+
615                        "Setting it to local engine ID");
616         }
617         scopedPDU.setContextEngineID(new OctetString(localEngineID));
618       }
619     }
620
621     // get length of scoped PDU
622
int scopedPDULength = pdu.getBERLength();
623     BEROutputStream scopedPdu =
624         new BEROutputStream(ByteBuffer.allocate(scopedPDULength));
625
626     scopedPDU.encodeBER(scopedPdu);
627
628     HeaderData headerData = new HeaderData();
629     int flags = 0;
630     switch (securityLevel) {
631       case SecurityLevel.NOAUTH_NOPRIV:
632         flags = 0;
633         break;
634       case SecurityLevel.AUTH_NOPRIV:
635         flags = 1;
636         break;
637       case SecurityLevel.AUTH_PRIV:
638         flags = 3;
639         break;
640     }
641     if (scopedPDU.isConfirmedPdu()) {
642       flags |= MPv3_REPORTABLE_FLAG;
643     }
644     else {
645       secEngineID = localEngineID;
646     }
647
648     int msgID = getNextMessageID();
649     headerData.setMsgFlags(flags);
650     headerData.setMsgID(msgID);
651     headerData.setMsgMaxSize(maxMessageSize);
652     headerData.setSecurityModel(securityModel);
653
654     ByteBuffer JavaDoc globalDataBuffer =
655         ByteBuffer.allocate(headerData.getBERLength());
656     BEROutputStream globalDataOutputStream =
657         new BEROutputStream(globalDataBuffer);
658     headerData.encodeBER(globalDataOutputStream);
659
660     BERInputStream scopedPDUInput = new BERInputStream(scopedPdu.rewind());
661
662     SecurityModel secModel =
663         securityModels.getSecurityModel(new Integer32(securityModel));
664     if (secModel == null) {
665       return SnmpConstants.SNMP_MP_UNSUPPORTED_SECURITY_MODEL;
666     }
667     // output data
668
SecurityParameters securityParameters =
669         secModel.newSecurityParametersInstance();
670
671     int status =
672         secModel.generateRequestMessage(messageProcessingModel,
673                                         globalDataBuffer.array(),
674                                         maxMessageSize,
675                                         securityModel,
676                                         secEngineID,
677                                         securityName,
678                                         securityLevel,
679                                         scopedPDUInput,
680                                         securityParameters,
681                                         outgoingMessage);
682     if (status == SnmpConstants.SNMPv3_USM_OK) {
683       if (expectResponse) {
684         cache.addEntry(new StateReference(msgID,
685                                           flags,
686                                           maxMessageSize,
687                                           sendPduHandle,
688                                           transportAddress,
689                                           null,
690                                           secEngineID, secModel,
691                                           securityName, securityLevel,
692                                           scopedPDU.getContextEngineID().
693                                           getValue(),
694                                           scopedPDU.getContextName().getValue(),
695                                           null,
696                                           status));
697       }
698     }
699     return status;
700   }
701
702   public int prepareResponseMessage(int messageProcessingModel,
703                                     int maxMessageSize,
704                                     int securityModel,
705                                     byte[] securityName,
706                                     int securityLevel,
707                                     PDU pdu,
708                                     int maxSizeResponseScopedPDU,
709                                     StateReference stateReference,
710                                     StatusInformation statusInformation,
711                                     BEROutputStream outgoingMessage) throws
712       IOException {
713     /** Leave entry in cache or remove it? RFC3414 3.1.a.1 says discard it*/
714     StateReference cacheEntry = cache.popEntry(stateReference.getMsgID());
715     if (cacheEntry == null) {
716       return SnmpConstants.SNMP_MP_UNKNOWN_MSGID;
717     }
718
719     // get length of scoped PDU
720
// get length of scoped PDU
721
int scopedPDULength = pdu.getBERLength();
722     BEROutputStream scopedPDU;
723     // check length
724
if (scopedPDULength > maxSizeResponseScopedPDU) {
725       PDU tooBigPDU = new ScopedPDU((ScopedPDU)pdu);
726       tooBigPDU.clear();
727       tooBigPDU.setRequestID(pdu.getRequestID());
728       tooBigPDU.setErrorStatus(SnmpConstants.SNMP_ERROR_TOO_BIG);
729       tooBigPDU.setErrorIndex(0);
730       scopedPDULength = tooBigPDU.getBERLength();
731       scopedPDU = new BEROutputStream(ByteBuffer.allocate(scopedPDULength));
732       tooBigPDU.encodeBER(scopedPDU);
733     }
734     else {
735       scopedPDU = new BEROutputStream(ByteBuffer.allocate(scopedPDULength));
736       pdu.encodeBER(scopedPDU);
737     }
738
739     HeaderData headerData = new HeaderData();
740     int flags = 0;
741     switch (securityLevel) {
742       case SecurityLevel.NOAUTH_NOPRIV:
743         flags = 0;
744         break;
745       case SecurityLevel.AUTH_NOPRIV:
746         flags = 1;
747         break;
748       case SecurityLevel.AUTH_PRIV:
749         flags = 3;
750         break;
751     }
752     // response message is not reportable
753
headerData.setMsgFlags(flags);
754     headerData.setMsgID(stateReference.getMsgID());
755     headerData.setMsgMaxSize(maxMessageSize);
756     headerData.setSecurityModel(securityModel);
757
758     ByteBuffer JavaDoc globalDataBuffer =
759         ByteBuffer.allocate(headerData.getBERLength());
760     BEROutputStream globalDataOutputStream =
761         new BEROutputStream(globalDataBuffer);
762     headerData.encodeBER(globalDataOutputStream);
763
764     OctetString securityEngineID;
765     switch (pdu.getType()) {
766       case PDU.RESPONSE:
767       case PDU.TRAP:
768       case PDU.REPORT:
769       case PDU.V1TRAP:
770         securityEngineID = new OctetString(localEngineID);
771         break;
772       default:
773         securityEngineID = new OctetString(cacheEntry.getSecurityEngineID());
774     }
775
776     BERInputStream scopedPDUInput = new BERInputStream(scopedPDU.rewind());
777
778     SecurityModel secModel =
779         securityModels.getSecurityModel(new Integer32(securityModel));
780     // output data
781
SecurityParameters securityParameters =
782         secModel.newSecurityParametersInstance();
783
784     int status =
785         secModel.generateResponseMessage(getID(),
786                                          globalDataBuffer.array(),
787                                          maxMessageSize,
788                                          securityModel,
789                                          securityEngineID.getValue(),
790                                          securityName,
791                                          securityLevel,
792                                          scopedPDUInput,
793                                          cacheEntry.getSecurityStateReference(),
794                                          securityParameters,
795                                          outgoingMessage);
796     return status;
797   }
798
799   /**
800    * Sends a report message.
801    * @param messageDispatcher
802    * Send the message on behalf the supplied MessageDispatcher instance.
803    * @param pdu ScopedPDU
804    * If <code>null</code>, then contextEngineID, contextName, and requestID
805    * of the report generated will be zero length and zero respective.
806    * Otherwise these values are extracted from the PDU.
807    * @param securityLevel
808    * The security level to use when sending this report.
809    * @param securityModel
810    * The security model to use when sending this report.
811    * @param securityName
812    * The security name to use when sending this report.
813    * @param maxSizeResponseScopedPDU
814    * the maximum size of of the report message (will be most likely ignored
815    * because a report should always fit in 484 bytes).
816    * @param payload
817    * the variable binding to include in the report message.
818    * @param stateReference
819    * the state reference associated with the original message.
820    * @return
821    * an SNMP MPv3 error code or 0 if the report has been send successfully.
822    */

823   public int sendReport(MessageDispatcher messageDispatcher,
824                         ScopedPDU pdu,
825                         int securityLevel,
826                         int securityModel,
827                         OctetString securityName,
828                         int maxSizeResponseScopedPDU,
829                         StateReference stateReference,
830                         VariableBinding payload) {
831     ScopedPDU reportPDU = new ScopedPDU();
832     reportPDU.setType(PDU.REPORT);
833     if (pdu != null) {
834       reportPDU.setContextEngineID(pdu.getContextEngineID());
835       reportPDU.setContextName(pdu.getContextName());
836       reportPDU.setRequestID(pdu.getRequestID());
837     }
838     reportPDU.add(payload);
839     StatusInformation statusInformation = new StatusInformation();
840     try {
841       int status = messageDispatcher.returnResponsePdu(getID(),
842           securityModel,
843           securityName.getValue(),
844           securityLevel,
845           reportPDU,
846           maxSizeResponseScopedPDU,
847           stateReference,
848           statusInformation);
849       if (status != SnmpConstants.SNMP_ERROR_SUCCESS) {
850         logger.warn("Error while sending report: " + status);
851         return SnmpConstants.SNMP_MP_ERROR;
852       }
853     }
854     catch (MessageException mex) {
855       logger.error("Error while sending report: " + mex.getMessage());
856       return SnmpConstants.SNMP_MP_ERROR;
857     }
858     return SnmpConstants.SNMP_MP_OK;
859   }
860
861   public int prepareDataElements(MessageDispatcher messageDispatcher,
862                                  Address transportAddress,
863                                  BERInputStream wholeMsg,
864                                  Integer32 messageProcessingModel,
865                                  Integer32 securityModel,
866                                  OctetString securityName,
867                                  Integer32 securityLevel,
868                                  MutablePDU pdu,
869                                  PduHandle sendPduHandle,
870                                  Integer32 maxSizeResponseScopedPDU,
871                                  StatusInformation statusInformation,
872                                  MutableStateReference mutableStateReference) {
873     try {
874       StateReference stateReference = new StateReference();
875       // check if there is transport mapping information
876
if (mutableStateReference.getStateReference() != null) {
877         stateReference.setTransportMapping(
878             mutableStateReference.getStateReference().getTransportMapping());
879       }
880       messageProcessingModel.setValue(MPv3);
881       wholeMsg.mark(16);
882
883       BER.MutableByte type = new BER.MutableByte();
884       int length = BER.decodeHeader(wholeMsg, type);
885       if (type.getValue() != BER.SEQUENCE) {
886         return SnmpConstants.SNMP_MP_PARSE_ERROR;
887       }
888       long lengthOfLength = wholeMsg.getPosition();
889       wholeMsg.reset();
890       wholeMsg.mark(length);
891       if (wholeMsg.skip(lengthOfLength) != lengthOfLength) {
892         return SnmpConstants.SNMP_MP_PARSE_ERROR;
893       }
894
895       Integer32 snmpVersion = new Integer32();
896       snmpVersion.decodeBER(wholeMsg);
897       if (snmpVersion.getValue() != SnmpConstants.version3) {
898         // internal error -> should not happen
899
throw new RuntimeException JavaDoc(
900             "Internal error unexpected snmp version read");
901       }
902       // decode SNMPv3 header
903
HeaderData header = new HeaderData();
904       header.decodeBER(wholeMsg);
905       securityModel.setValue(header.getSecurityModel());
906
907       stateReference.setMsgID(header.getMsgID());
908       stateReference.setMsgFlags(header.getMsgFlags());
909       stateReference.setAddress(transportAddress);
910
911       mutableStateReference.setStateReference(stateReference);
912
913       // the usm has to recalculate this value
914
maxSizeResponseScopedPDU.setValue(header.msgMaxSize.getValue() -
915                                         MAX_HEADER_LENGTH);
916
917       ScopedPDU scopedPdu = new ScopedPDU();
918       pdu.setPdu(scopedPdu);
919
920       SecurityModel secModel = securityModels.getSecurityModel(securityModel);
921       if (secModel == null) {
922         logger.error("RFC3412 7.2.4 - Unsupported security model: " +
923                      securityModel);
924         CounterEvent event =
925             new CounterEvent(this,
926                              SnmpConstants.snmpUnknownSecurityModels);
927         fireIncrementCounter(event);
928         return SnmpConstants.SNMP_MP_UNSUPPORTED_SECURITY_MODEL;
929       }
930
931       // determine security level
932
switch (header.getMsgFlags() & 0x03) {
933         case 3: {
934           securityLevel.setValue(SecurityLevel.AUTH_PRIV);
935           break;
936         }
937         case 0: {
938           securityLevel.setValue(SecurityLevel.NOAUTH_NOPRIV);
939           break;
940         }
941         case 1: {
942           securityLevel.setValue(SecurityLevel.AUTH_NOPRIV);
943           break;
944         }
945         default: {
946           securityLevel.setValue(SecurityLevel.NOAUTH_NOPRIV);
947           logger.debug("RFC3412 7.2.5 - Invalid message (illegal msgFlags)");
948           CounterEvent event = new CounterEvent(this,
949                                                 SnmpConstants.snmpInvalidMsgs);
950           fireIncrementCounter(event);
951           // do not send back report
952
return SnmpConstants.SNMP_MP_INVALID_MESSAGE;
953         }
954       }
955
956       int secParametersPosition = (int)wholeMsg.getPosition();
957       // get security parameters
958
SecurityParameters secParameters =
959           secModel.newSecurityParametersInstance();
960       secParameters.decodeBER(wholeMsg);
961       secParameters.setSecurityParametersPosition(secParametersPosition);
962
963       // reportable flag
964
boolean reportableFlag = ((header.getMsgFlags() & 0x04) > 0);
965
966       OctetString securityEngineID = new OctetString();
967       // create a new security state reference
968
SecurityStateReference secStateReference =
969           secModel.newSecurityStateReference();
970       // create output stream for scoped PDU
971
// may be optimized by an output stream that maps directly into the
972
// original input
973
wholeMsg.reset();
974
975       BEROutputStream scopedPDU = new BEROutputStream();
976       int status =
977           secModel.processIncomingMsg(snmpVersion.getValue(),
978                                       header.getMsgMaxSize() -
979                                       MAX_HEADER_LENGTH,
980                                       secParameters,
981                                       secModel,
982                                       securityLevel.getValue(),
983                                       wholeMsg,
984                                       // output parameters
985
securityEngineID,
986                                       securityName,
987                                       scopedPDU,
988                                       maxSizeResponseScopedPDU,
989                                       secStateReference,
990                                       statusInformation);
991       wholeMsg.close();
992       if (status == SnmpConstants.SNMPv3_USM_OK) {
993         try {
994           BERInputStream scopedPduStream =
995               new BERInputStream(scopedPDU.rewind());
996           scopedPdu.decodeBER(scopedPduStream);
997           sendPduHandle.setTransactionID(scopedPdu.getRequestID().getValue());
998
999           // add the engine ID to the local cache.
1000
addEngineID(transportAddress, securityEngineID);
1001        }
1002        catch (IOException iox) {
1003          logger.warn("ASN.1 parse error: "+iox.getMessage());
1004          if (logger.isDebugEnabled()) {
1005            iox.printStackTrace();
1006          }
1007          CounterEvent event = new CounterEvent(this,
1008                                                SnmpConstants.
1009                                                snmpInASNParseErrs);
1010          fireIncrementCounter(event);
1011          return SnmpConstants.SNMP_MP_PARSE_ERROR;
1012        }
1013        if (((scopedPdu.getContextEngineID() == null) ||
1014              (scopedPdu.getContextEngineID().length() == 0)) &&
1015            ((scopedPdu.getType() != PDU.RESPONSE) &&
1016             (scopedPdu.getType() != PDU.REPORT))) {
1017          CounterEvent event = new CounterEvent(this,
1018                                                SnmpConstants.
1019                                                snmpUnknownPDUHandlers);
1020          fireIncrementCounter(event);
1021          VariableBinding errorIndication =
1022              new VariableBinding(event.getOid(), event.getCurrentValue());
1023          statusInformation.setErrorIndication(errorIndication);
1024          status = SnmpConstants.SNMP_MP_UNKNOWN_PDU_HANDLERS;
1025        }
1026      }
1027
1028      if (status != SnmpConstants.SNMPv3_USM_OK) {
1029        if ((reportableFlag) &&
1030            (statusInformation.getErrorIndication() != null)) {
1031          // RFC3412 7.2.6.a - generate a report
1032
try {
1033            if (scopedPDU.getBuffer() != null) {
1034              BERInputStream scopedPduStream =
1035                  new BERInputStream(scopedPDU.rewind());
1036              scopedPdu.decodeBER(scopedPduStream);
1037            }
1038            else { // incoming message could not be decoded
1039
scopedPdu = null;
1040            }
1041          }
1042          catch (IOException iox) {
1043            logger.warn(iox);
1044            scopedPdu = null;
1045          }
1046
1047          StateReference cacheEntry =
1048              new StateReference(header.getMsgID(),
1049                                 header.getMsgFlags(),
1050                                 maxSizeResponseScopedPDU.getValue(),
1051                                 sendPduHandle,
1052                                 transportAddress,
1053                                 null,
1054                                 securityEngineID.getValue(),
1055                                 secModel, securityName.getValue(),
1056                                 securityLevel.getValue(),
1057                                 (scopedPdu == null) ? new byte[0] :
1058                                 scopedPdu.getContextEngineID().getValue(),
1059                                 (scopedPdu == null) ? new byte[0] :
1060                                 scopedPdu.getContextName().getValue(),
1061                                 secStateReference, status);
1062          cache.addEntry(cacheEntry);
1063
1064          int reportStatus =
1065              sendReport(messageDispatcher, scopedPdu,
1066                         statusInformation.getSecurityLevel().getValue(),
1067                         secModel.getID(), securityName,
1068                         maxSizeResponseScopedPDU.getValue(),
1069                         stateReference,
1070                         statusInformation.getErrorIndication());
1071          if (reportStatus != SnmpConstants.SNMP_MP_OK) {
1072            logger.warn("Sending report failed with error code: " +
1073                        reportStatus);
1074          }
1075        }
1076        return SnmpConstants.SNMP_MP_USM_ERROR;
1077      }
1078
1079      stateReference.setAddress(transportAddress);
1080      stateReference.setSecurityName(securityName.getValue());
1081      stateReference.setContextEngineID(scopedPdu.getContextEngineID().getValue());
1082      stateReference.setContextName(scopedPdu.getContextName().getValue());
1083      stateReference.setMaxSizeResponseScopedPDU(maxSizeResponseScopedPDU.
1084                                                 getValue());
1085      stateReference.setMsgID(header.getMsgID());
1086      stateReference.setMsgFlags(header.getMsgFlags());
1087      stateReference.setSecurityEngineID(securityEngineID.getValue());
1088      stateReference.setSecurityLevel(securityLevel.getValue());
1089      stateReference.setSecurityModel(secModel);
1090      stateReference.setSecurityStateReference(secStateReference);
1091      stateReference.setPduHandle(sendPduHandle);
1092
1093      if ((scopedPdu.getType() == PDU.RESPONSE) ||
1094          (scopedPdu.getType() == PDU.REPORT)) {
1095        StateReference cacheEntry = cache.popEntry(header.getMsgID());
1096        if (cacheEntry != null) {
1097          if (logger.isDebugEnabled()) {
1098            logger.debug("RFC3412 7.2.10 - Received PDU (msgID=" +
1099                         header.getMsgID() + ") is a response or " +
1100                         "an internal class message. PduHandle.transactionID = " +
1101                         cacheEntry.getPduHandle().getTransactionID());
1102          }
1103          sendPduHandle.copyFrom(cacheEntry.getPduHandle());
1104
1105          if (scopedPdu.getType() == PDU.REPORT) {
1106
1107            statusInformation.setContextEngineID(scopedPdu.getContextEngineID().getValue());
1108            statusInformation.setContextName(scopedPdu.getContextName().getValue());
1109            statusInformation.setSecurityLevel(securityLevel);
1110
1111            if (((cacheEntry.getSecurityEngineID().length != 0) &&
1112                  (!securityEngineID.equals(cacheEntry.getSecurityEngineID()))) ||
1113                (secModel.getID() != cacheEntry.getSecurityModel().getID()) ||
1114                ((!securityName.equals(cacheEntry.getSecurityName()) &&
1115                  (securityName.length() != 0)))) {
1116              if (logger.isDebugEnabled()) {
1117                logger.debug(
1118                    "RFC 3412 7.2.11 - Received report message does not match sent message");
1119              }
1120              //cache.deleteEntry(cacheEntry.getPduHandle());
1121
mutableStateReference.setStateReference(null);
1122              return SnmpConstants.SNMP_MP_MATCH_ERROR;
1123            }
1124            addEngineID(cacheEntry.getAddress(), securityEngineID);
1125            //cache.deleteEntry(cacheEntry.getPduHandle());
1126
mutableStateReference.setStateReference(null);
1127            logger.debug("MPv3 finished");
1128            return SnmpConstants.SNMP_MP_OK;
1129          }
1130          if (scopedPdu.getType() == PDU.RESPONSE) {
1131            if (((!securityEngineID.equals(cacheEntry.getSecurityEngineID())) &&
1132                 (cacheEntry.getSecurityEngineID().length != 0)) ||
1133                (secModel.getID() != cacheEntry.getSecurityModel().getID()) ||
1134                (!securityName.equals(cacheEntry.getSecurityName())) ||
1135                (securityLevel.getValue() != cacheEntry.getSecurityLevel()) ||
1136                ((!scopedPdu.getContextEngineID().equals(cacheEntry.getContextEngineID())) &&
1137                 (cacheEntry.getContextEngineID().length != 0)) ||
1138                ((!scopedPdu.getContextName().equals(cacheEntry.getContextName()) &&
1139                  (cacheEntry.getContextName().length != 0)))) {
1140              logger.debug(
1141                  "RFC 3412 7.2.12.b - Received response message does not match sent message");
1142              //cache.deleteEntry(cacheEntry.getPduHandle());
1143
mutableStateReference.setStateReference(null);
1144              return SnmpConstants.SNMP_MP_MATCH_ERROR;
1145            }
1146            //cache.deleteEntry(cacheEntry.getPduHandle());
1147
mutableStateReference.setStateReference(null);
1148            logger.debug("MPv3 finished");
1149            return SnmpConstants.SNMP_MP_OK;
1150          }
1151        }
1152        else {
1153          if (logger.isDebugEnabled()) {
1154            logger.debug("RFC3412 7.2.10 - Received PDU (msgID=" +
1155                         header.getMsgID() + ") is a response or " +
1156                         "internal class message, but cached " +
1157                         "information for the msgID could not be found");
1158          }
1159          return SnmpConstants.SNMP_MP_UNKNOWN_MSGID;
1160        }
1161      }
1162      else {
1163        logger.debug("RFC3412 7.2.10 - Received PDU is NOT a response or " +
1164                     "internal class message -> unchanged PduHandle = "+
1165                     sendPduHandle);
1166      }
1167      switch (scopedPdu.getType()) {
1168        case PDU.GET:
1169        case PDU.GETBULK:
1170        case PDU.GETNEXT:
1171        case PDU.INFORM:
1172        case PDU.SET: {
1173          if (securityEngineID.length() == 0) {
1174            logger.debug("Received confirmed message with 0 length security engine ID");
1175          }
1176          else if (!securityEngineID.equals(localEngineID)) {
1177            if (logger.isDebugEnabled()) {
1178              logger.debug("RFC 3412 7.2.13.a - Security engine ID " +
1179                           securityEngineID.toHexString() +
1180                           " does not match local engine ID " +
1181                           new OctetString(localEngineID).toHexString());
1182            }
1183            mutableStateReference.setStateReference(null);
1184            return SnmpConstants.SNMP_MP_INVALID_ENGINEID;
1185          }
1186          int cacheStatus = cache.addEntry(stateReference);
1187          if (cacheStatus == SnmpConstants.SNMP_MP_DOUBLED_MESSAGE) {
1188            mutableStateReference.setStateReference(null);
1189          }
1190          return SnmpConstants.SNMP_MP_OK;
1191        }
1192        case PDU.TRAP:
1193        case PDU.V1TRAP: {
1194          mutableStateReference.setStateReference(null);
1195          return SnmpConstants.SNMP_MP_OK;
1196        }
1197      }
1198      // this line should not be reached
1199
return SnmpConstants.SNMP_MP_ERROR;
1200    }
1201    catch (IOException iox) {
1202      logger.warn("MPv3 parse error: " + iox.getMessage());
1203      if (logger.isDebugEnabled()) {
1204        iox.printStackTrace();
1205      }
1206      return SnmpConstants.SNMP_MP_PARSE_ERROR;
1207    }
1208  }
1209
1210  /**
1211   * Sets the security models supported by this MPv3.
1212   * @param securityModels
1213   * a <code>SecurityModels</code> instance.
1214   */

1215  public void setSecurityModels(SecurityModels securityModels) {
1216    this.securityModels = securityModels;
1217  }
1218
1219  /**
1220   * Gets the security models supported by this MPv3.
1221   * @return
1222   * a <code>SecurityModels</code> instance.
1223   */

1224  public SecurityModels getSecurityModels() {
1225    return securityModels;
1226  }
1227
1228  /**
1229   * Gets the enterprise ID used for creating the local engine ID.
1230   * @return
1231   * an enterprise ID as registered by the IANA (see http://www.iana.org).
1232   */

1233  public static int getEnterpriseID() {
1234    return enterpriseID;
1235  }
1236
1237  /**
1238   * Sets the IANA enterprise ID to be used for creating local engine ID by
1239   * {@link #createLocalEngineID()}.
1240   * @param newEnterpriseID
1241   * an enterprise ID as registered by the IANA (see http://www.iana.org).
1242   */

1243  public static void setEnterpriseID(int newEnterpriseID) {
1244    enterpriseID = newEnterpriseID;
1245  }
1246
1247  /**
1248   * Fire a counter incrementation event.
1249   * @param e CounterEvent
1250   */

1251  protected void fireIncrementCounter(CounterEvent e) {
1252    counterSupport.fireIncrementCounter(e);
1253  }
1254
1255  /**
1256   * Gets the counter support instance that can be used to register for
1257   * counter incremnetation events.
1258   * @return
1259   * a <code>CounterSupport</code> instance that is used to fire
1260   * {@link CounterEvent}.
1261   */

1262  public CounterSupport getCounterSupport() {
1263    return counterSupport;
1264  }
1265
1266  /**
1267   * Sets the counter support instance. By default, the singleton instance
1268   * provided by the {@link CounterSupport} instance is used.
1269   * @param counterSupport
1270   * a <code>CounterSupport</code> subclass instance.
1271   */

1272  public void setCounterSupport(CounterSupport counterSupport) {
1273    if (counterSupport == null) {
1274      throw new NullPointerException JavaDoc();
1275    }
1276    this.counterSupport = counterSupport;
1277  }
1278
1279  /**
1280   * Adds a SNMP engine listener that needs to be informed about changes to
1281   * the engine ID cache.
1282   * @param l
1283   * a <code>SnmpEngineListener</code> instance.
1284   * @since 1.6
1285   */

1286  public synchronized void addSnmpEngineListener(SnmpEngineListener l) {
1287    if (snmpEngineListeners == null) {
1288      snmpEngineListeners = new Vector();
1289    }
1290    snmpEngineListeners.add(l);
1291  }
1292
1293  /**
1294   * Removes a SNMP engine listener.
1295   * @param l
1296   * a <code>SnmpEngineListener</code> instance.
1297   * @since 1.6
1298   */

1299  public synchronized void removeSnmpEngineListener(SnmpEngineListener l) {
1300    if (snmpEngineListeners != null) {
1301      snmpEngineListeners.remove(l);
1302    }
1303  }
1304
1305  /**
1306   * Fires a SNMP engine event the registered listeners.
1307   * @param engineEvent
1308   * the <code>SnmpEngineEvent</code> instance to fire.
1309   * @since 1.6
1310   */

1311  protected void fireEngineChanged(SnmpEngineEvent engineEvent) {
1312    if (snmpEngineListeners != null) {
1313      Vector listeners = snmpEngineListeners;
1314      int count = listeners.size();
1315      for (int i = 0; i < count; i++) {
1316        ((SnmpEngineListener) listeners.elementAt(i)).engineChanged(engineEvent);
1317      }
1318    }
1319  }
1320}
1321
Popular Tags