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 }