View Javadoc
1   // NAME
2   //      $RCSfile: SnmpContextv3.java,v $
3   // DESCRIPTION
4   //      [given below in javadoc format]
5   // DELTA
6   //      $Revision: 3.31 $
7   // CREATED
8   //      $Date: 2009/03/05 13:12:50 $
9   // COPYRIGHT
10  //      Westhawk Ltd
11  // TO DO
12  //
13  
14  /*
15   * Copyright (C) 2000 - 2006 by Westhawk Ltd
16   * <a href="www.westhawk.co.uk">www.westhawk.co.uk</a>
17   *
18   * Permission to use, copy, modify, and distribute this software
19   * for any purpose and without fee is hereby granted, provided
20   * that the above copyright notices appear in all copies and that
21   * both the copyright notice and this permission notice appear in
22   * supporting documentation.
23   * This software is provided "as is" without express or implied
24   * warranty.
25   */
26  
27  package uk.co.westhawk.snmp.stack;
28  
29  /*-
30   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
31   * SNMP Java Client
32   * ჻჻჻჻჻჻
33   * Copyright 2023 MetricsHub, Westhawk
34   * ჻჻჻჻჻჻
35   * This program is free software: you can redistribute it and/or modify
36   * it under the terms of the GNU Lesser General Public License as
37   * published by the Free Software Foundation, either version 3 of the
38   * License, or (at your option) any later version.
39   *
40   * This program is distributed in the hope that it will be useful,
41   * but WITHOUT ANY WARRANTY; without even the implied warranty of
42   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
43   * GNU General Lesser Public License for more details.
44   *
45   * You should have received a copy of the GNU General Lesser Public
46   * License along with this program.  If not, see
47   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
48   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
49   */
50  
51  import java.io.*;
52  
53  import uk.co.westhawk.snmp.event.*;
54  import uk.co.westhawk.snmp.beans.*;
55  
56  /**
57   * This class contains the SNMP v3 context that is needed by every PDU to
58   * send a SNMP v3 request.
59   * Most of the work is done by SnmpContextv3Basis, like doing discovery. 
60   *
61   * <p>
62   * Now that the stack can send traps and receive requests, 
63   * it needs to be able to act as an
64   * authoritative SNMP engine. This is done via the interface UsmAgent.
65   * The DefaultUsmAgent is not guaranteed to work; agents (or rather 
66   * authoritative engines) <em>should</em> provide a better implementation.
67   * </p>
68   *
69   * <p>
70   * This class adds a UsmBeingDiscoveredBean as listener. This bean
71   * handles any incoming discovery PDU. Only when acting as
72   * authoritative engine should there be any discovery PDU.
73   * </p>
74   *
75   * @see SnmpContextv3Face
76   * @see SnmpContextv3Pool
77   * @see TimeWindow
78   * @see DefaultUsmAgent
79   * @see UsmAgent
80   * @see #setUsmAgent(UsmAgent)
81   * @see UsmDiscoveryBean
82   *
83   * @author <a href="mailto:snmp@westhawk.co.uk">Birgit Arkesteijn</a>
84   * @version $Revision: 3.31 $ $Date: 2009/03/05 13:12:50 $
85   */
86  public class SnmpContextv3 extends SnmpContextv3Basis {
87      private static final String version_id = "@(#)$Id: SnmpContextv3.java,v 3.31 2009/03/05 13:12:50 birgita Exp $ Copyright Westhawk Ltd";
88  
89      private UsmBeingDiscoveredBean myDiscBean = null;
90  
91      /**
92       * Constructor.
93       *
94       * @param host The host to which the PDU will be sent
95       * @param port The port where the SNMP server will be
96       * @see AbstractSnmpContext#AbstractSnmpContext(String, int)
97       */
98      public SnmpContextv3(String host, int port) throws IOException {
99          super(host, port);
100     }
101 
102     /**
103      * Constructor.
104      * Parameter typeSocketA should be either STANDARD_SOCKET, TCP_SOCKET or a
105      * fully qualified classname.
106      *
107      * @param host        The host to which the Pdu will be sent
108      * @param port        The port where the SNMP server will be
109      * @param typeSocketA The local address the server will bind to
110      *
111      * @see AbstractSnmpContext#AbstractSnmpContext(String, int, String)
112      */
113     public SnmpContextv3(String host, int port, String typeSocketA)
114             throws IOException {
115         super(host, port, typeSocketA);
116     }
117 
118     /**
119      * Constructor.
120      * Parameter typeSocketA should be either STANDARD_SOCKET, TCP_SOCKET or a
121      * fully qualified classname.
122      *
123      * @param host        The host to which the PDU will be sent
124      * @param port        The port where the SNMP server will be
125      * @param bindAddress The local address the server will bind to
126      * @param typeSocketA The type of socket to use.
127      *
128      * @see AbstractSnmpContext#AbstractSnmpContext(String, int, String, String)
129      * @see SnmpContextBasisFace#STANDARD_SOCKET
130      * @see SnmpContextBasisFace#TCP_SOCKET
131      * @since 4_14
132      */
133     public SnmpContextv3(String host, int port, String bindAddress, String typeSocketA)
134             throws IOException {
135         super(host, port, bindAddress, typeSocketA);
136     }
137 
138     /**
139      * Makes sure the UsmBeingDiscoveredBean is added as RequestPduListener,
140      * so that discovery requests are handled. When listening for incoming
141      * requests, the stack become authoritative. You have (!) to create a
142      * proper usmAgent so the stack can be discovered.
143      *
144      * <p>
145      * Don't use the TCP_SOCKET when listening for request PDUs. It doesn't
146      * provide functionality to send a response back.
147      * </p>
148      *
149      * @see #removeRequestPduListener(RequestPduListener, ListeningContextPool)
150      * @see UsmBeingDiscoveredBean
151      * @see SnmpContextv3Basis#setUsmAgent(UsmAgent)
152      *
153      * @param l        The request PDU listener
154      * @param lcontext The listening context
155      */
156     public void addRequestPduListener(RequestPduListener l, ListeningContextPool lcontext)
157             throws IOException {
158         super.addRequestPduListener(l, lcontext);
159 
160         if (myDiscBean == null) {
161             myDiscBean = new UsmBeingDiscoveredBean(this, usmAgent);
162         }
163         myDiscBean.addRequestPduListener(lcontext);
164     }
165 
166     /**
167      * Removes the UsmBeingDiscoveredBean as listener.
168      *
169      * @see #addRequestPduListener(RequestPduListener, ListeningContextPool)
170      *
171      * @param l        The request PDU listener
172      * @param lcontext The listening context
173      */
174     public void removeRequestPduListener(RequestPduListener l, ListeningContextPool lcontext)
175             throws IOException {
176         super.removeRequestPduListener(l, lcontext);
177         if (myDiscBean != null) {
178             myDiscBean.removeRequestPduListener(lcontext);
179             myDiscBean.freeResources();
180             myDiscBean = null;
181         }
182     }
183 
184     /**
185      * Processes an incoming PDU, that is <em>not</em> a Discovery PDU.
186      * <p>
187      * See <a href="http://www.ietf.org/rfc/rfc3414.txt">SNMP-USER-BASED-SM-MIB</a>.
188      * </p>
189      *
190      * <p>
191      * This method calls first processPotentialTrap and then
192      * processPotentialRequest. The reason this code is split up is because
193      * in one case the stack acts as authoritative engine and as non
194      * authoritative engine in the other..
195      * </p>
196      *
197      * @see #rawPduReceived
198      * @see #processPotentialTrap
199      * @see #processPotentialRequest
200      */
201     public Pdu processIncomingPdu(byte[] message)
202             throws DecodingException, IOException {
203         String msg = checkContextSanity();
204         if (msg != null) {
205             throw new DecodingException(msg);
206         }
207         int l = message.length;
208         byte[] copyOfMessage1 = new byte[l];
209         byte[] copyOfMessage2 = new byte[l];
210         System.arraycopy(message, 0, copyOfMessage1, 0, l);
211         System.arraycopy(message, 0, copyOfMessage2, 0, l);
212 
213         AsnDecoderv3 rpdu = new AsnDecoderv3();
214         ByteArrayInputStream in = new ByteArrayInputStream(message);
215         AsnSequence asnTopSeq = rpdu.DecodeSNMPv3(in);
216         int messageId = rpdu.getMessageId(asnTopSeq);
217 
218         Pdu pdu = null;
219 
220         DecodingException encryptionDecodingException1 = null;
221         IOException encryptionIOException1 = null;
222         try {
223             pdu = processPotentialTrap(rpdu, asnTopSeq, copyOfMessage1);
224         } catch (DecodingException exc) {
225             encryptionDecodingException1 = exc;
226             if (AsnObject.debug > 3) {
227                 System.out.println(getClass().getName()
228                         + ".processPotentialTrap(): DecodingException: "
229                         + exc.getMessage());
230             }
231         } catch (IOException exc) {
232             encryptionIOException1 = exc;
233             if (AsnObject.debug > 3) {
234                 System.out.println(getClass().getName()
235                         + ".processPotentialTrap(): IOException: "
236                         + exc.getMessage());
237             }
238         }
239 
240         DecodingException encryptionDecodingException2 = null;
241         IOException encryptionIOException2 = null;
242         if (pdu == null) {
243             try {
244                 pdu = processPotentialRequest(rpdu, asnTopSeq, copyOfMessage2);
245             } catch (DecodingException exc) {
246                 encryptionDecodingException2 = exc;
247                 if (AsnObject.debug > 3) {
248                     System.out.println(getClass().getName()
249                             + ".processPotentialRequest(): DecodingException: "
250                             + exc.getMessage());
251                 }
252             } catch (IOException exc) {
253                 encryptionIOException2 = exc;
254                 if (AsnObject.debug > 3) {
255                     System.out.println(getClass().getName()
256                             + ".processPotentialRequest(): IOException: "
257                             + exc.getMessage());
258                 }
259             }
260         }
261 
262         if (pdu != null) {
263             pdu.snmpv3MsgId = new Integer(messageId);
264         } else {
265             if (encryptionIOException2 != null) {
266                 throw encryptionIOException2;
267             }
268             if (encryptionDecodingException2 != null) {
269                 throw encryptionDecodingException2;
270             }
271             if (encryptionIOException1 != null) {
272                 throw encryptionIOException1;
273             }
274             if (encryptionDecodingException1 != null) {
275                 throw encryptionDecodingException1;
276             }
277         }
278         return pdu;
279     }
280 
281     /**
282      * Processes an incoming PDU, to see if it is a Trap.
283      * This method is called by processIncomingPdu.
284      *
285      * When receiving traps the stack is non authoritative.
286      *
287      * @see #processIncomingPdu
288      * @see #processPotentialRequest
289      * @since 4_14
290      */
291     public Pdu processPotentialTrap(AsnDecoderv3 rpdu, AsnSequence asnTopSeq,
292             byte[] message)
293             throws DecodingException, IOException {
294         // decode as Non Authoratative engine
295         AsnPduSequence pduSeq = rpdu.processSNMPv3(this, asnTopSeq, message, false);
296         Pdu pdu = null;
297         if (pduSeq != null) {
298             byte type = pduSeq.getRespType();
299             if (type == SnmpConstants.TRPV2_REQ_MSG) {
300                 pdu = new TrapPduv2(this);
301                 pdu.fillin(pduSeq);
302 
303                 if (AsnObject.debug > 3) {
304                     System.out.println(getClass().getName()
305                             + ".processPotentialTrap(): PDU received with type "
306                             + pduSeq.getRespTypeString()
307                             + ". Not ignoring it!");
308                 }
309             } else {
310                 if (AsnObject.debug > 3) {
311                     System.out.println(getClass().getName()
312                             + ".processPotentialTrap(): PDU received is not TRPV2_REQ_MSG"
313                             + ". Ignoring it.");
314                 }
315             }
316         } else {
317             if (AsnObject.debug > 3) {
318                 System.out.println(getClass().getName()
319                         + ".processPotentialTrap(): pduSeq == null"
320                         + ". Ignoring it.");
321             }
322         }
323         return pdu;
324     }
325 
326     /**
327      * Processes an incoming PDU, to see if it is a Request.
328      * This method is called by processIncomingPdu.
329      *
330      * When receiving pdu requests the stack is authoritative.
331      *
332      * @see #processIncomingPdu
333      * @see #processPotentialTrap
334      * @since 4_14
335      */
336     public Pdu processPotentialRequest(AsnDecoderv3 rpdu, AsnSequence asnTopSeq,
337             byte[] message)
338             throws DecodingException, IOException {
339         // decode as Authoratative engine
340         AsnPduSequence pduSeq = rpdu.processSNMPv3(this, asnTopSeq, message, true);
341         Pdu pdu = null;
342         if (pduSeq != null) {
343             byte type = pduSeq.getRespType();
344             if (type == SnmpConstants.GET_REQ_MSG && pduSeq.isSnmpv3Discovery() == true) {
345                 if (AsnObject.debug > 3) {
346                     System.out.println(getClass().getName()
347                             + ".ProcessIncomingPdu(): received discovery pdu"
348                             + ". Ignoring it.");
349                 }
350             } else {
351                 switch (type) {
352                     case SnmpConstants.GET_REQ_MSG:
353                         pdu = new GetPdu(this);
354                         pdu.fillin(pduSeq);
355                         break;
356                     case SnmpConstants.GETNEXT_REQ_MSG:
357                         pdu = new GetNextPdu(this);
358                         pdu.fillin(pduSeq);
359                         break;
360                     case SnmpConstants.SET_REQ_MSG:
361                         pdu = new SetPdu(this);
362                         pdu.fillin(pduSeq);
363                         break;
364                     case SnmpConstants.GETBULK_REQ_MSG:
365                         pdu = new GetBulkPdu(this);
366                         pdu.fillin(pduSeq);
367                         break;
368                     case SnmpConstants.INFORM_REQ_MSG:
369                         pdu = new InformPdu(this);
370                         pdu.fillin(pduSeq);
371                         break;
372                     // case SnmpConstants.GET_RSP_MSG:
373                     // A lonely response should never be received here.
374                     // They should come in via the processIncomingResponse
375                     // route.
376                     // case SnmpConstants.GET_RPRT_MSG:
377                     // Reports are part of v3 timeliness communication.
378                     // case SnmpConstants.TRPV2_REQ_MSG:
379                     // Traps should have been decoded in
380                     // processPotentialTrap() above
381                     default:
382                         if (AsnObject.debug > 3) {
383                             System.out.println(getClass().getName()
384                                     + ".processPotentialRequest(): PDU received with type "
385                                     + pduSeq.getRespTypeString()
386                                     + ". Ignoring it.");
387                         }
388                 }
389 
390                 if (pdu != null) {
391                     if (AsnObject.debug > 3) {
392                         System.out.println(getClass().getName()
393                                 + ".processPotentialRequest(): PDU received with type "
394                                 + pduSeq.getRespTypeString()
395                                 + ". Not ignoring it!");
396                     }
397                 }
398             }
399         } else {
400             if (AsnObject.debug > 3) {
401                 System.out.println(getClass().getName()
402                         + "..processPotentialRequest(): pduSeq == null"
403                         + ". Ignoring it.");
404             }
405         }
406         return pdu;
407     }
408 
409     /**
410      * Returns a clone of this SnmpContextv3.
411      *
412      * @exception CloneNotSupportedException Thrown when the constructor
413      *                                       generates an IOException
414      */
415     public Object clone() throws CloneNotSupportedException {
416         SnmpContextv3 clContext = null;
417         try {
418             clContext = new SnmpContextv3(hostname, hostPort, bindAddr, typeSocket);
419             clContext = (SnmpContextv3) cloneParameters(clContext);
420         } catch (IOException exc) {
421             throw new CloneNotSupportedException("IOException "
422                     + exc.getMessage());
423         }
424         return clContext;
425     }
426 
427 }