1 // NAME
2 // $RCSfile: AbstractSnmpContext.java,v $
3 // DESCRIPTION
4 // [given below in javadoc format]
5 // DELTA
6 // $Revision: 3.33 $
7 // CREATED
8 // $Date: 2009/03/05 12:48:04 $
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 * author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
26 */
27
28 package uk.co.westhawk.snmp.stack;
29
30 /*-
31 * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
32 * SNMP Java Client
33 * ჻჻჻჻჻჻
34 * Copyright 2023 MetricsHub, Westhawk
35 * ჻჻჻჻჻჻
36 * This program is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU Lesser General Public License as
38 * published by the Free Software Foundation, either version 3 of the
39 * License, or (at your option) any later version.
40 *
41 * This program is distributed in the hope that it will be useful,
42 * but WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44 * GNU General Lesser Public License for more details.
45 *
46 * You should have received a copy of the GNU General Lesser Public
47 * License along with this program. If not, see
48 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
49 * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
50 */
51
52 import java.io.*;
53 import java.util.*;
54 import uk.co.westhawk.snmp.net.*;
55 import uk.co.westhawk.snmp.event.*;
56 import uk.co.westhawk.snmp.util.*;
57
58 /**
59 * This class contains the abstract SNMP context that is needed by every
60 * Pdu to send a SNMP v1, v2c or v3 request.
61 * The context also provides functionality to receive PDUs.
62 *
63 * <p>
64 * <code>destroy()</code> should be called when the context is no longer
65 * used. This is the only way the threads will be stopped and garbage
66 * collected.
67 * </p>
68 *
69 * @see SnmpContext
70 * @see SnmpContextv2c
71 * @see SnmpContextv3
72 *
73 * @author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
74 * @version $Revision: 3.33 $ $Date: 2009/03/05 12:48:04 $
75 */
76 public abstract class AbstractSnmpContext extends Object
77 implements SnmpContextBasisFace, Runnable, RawPduListener {
78 private static final String version_id = "@(#)$Id: AbstractSnmpContext.java,v 3.33 2009/03/05 12:48:04 birgita Exp $ Copyright Westhawk Ltd";
79
80 private ContextSocketFace soc;
81 private Transmitter[] transmitters;
82 private Pdu[] pdus;
83 private Thread me;
84 private String basename;
85 private volatile boolean stopRequested;
86 // thanks to Nick Sheen nsheen@tippingpoint.com for pointing out that volatile
87 // is needed here
88
89 protected String typeSocket;
90 protected String hostname;
91 protected String bindAddr;
92 protected int hostPort;
93 protected int maxRecvSize;
94 protected boolean isDestroyed;
95 protected boolean anyPduExpectingResponse = false;
96 protected RequestPduReceivedSupport pduSupport;
97 protected TrapReceivedSupport trapSupport;
98
99 /**
100 * Processes an incoming response. Has to be overload by each context.
101 * This is called in the run() method.
102 *
103 * @see #run
104 */
105 protected abstract void processIncomingResponse(ByteArrayInputStream in)
106 throws DecodingException, IOException;
107
108 /**
109 * Encodes a PDU. This is for internal use only and should
110 * NOT be called by the developer.
111 * This is called by the the PDU itself and is added to the interface to
112 * cover the different kind of Contexts.
113 * Has to be overload by each context.
114 *
115 * @param msg_type The message type
116 * @param rId The message id
117 * @param errstat The error status
118 * @param errind The error index
119 * @param ve The varbind list
120 * @param obj Additional object (only used in SNMPv3)
121 * @return The encoded packet
122 */
123 public abstract byte[] encodePacket(byte msg_type, int rId, int errstat,
124 int errind, Enumeration ve, Object obj)
125 throws IOException, EncodingException;
126
127 /**
128 * Processes an incoming pdu (but not a response). Has to be overload by each
129 * context.
130 *
131 * @see #rawPduReceived
132 */
133 public abstract Pdu processIncomingPdu(byte[] message) throws DecodingException, IOException;
134
135 /**
136 * Returns the SNMP version of this context. Has to be overload by each
137 * context.
138 */
139 public abstract int getVersion();
140
141 /**
142 * Constructor.
143 * The Standard socket type will be used.
144 *
145 * @param host The host to which the Pdu will be sent
146 * @param port The port where the SNMP server will be
147 * @see SnmpContextBasisFace#STANDARD_SOCKET
148 */
149 protected AbstractSnmpContext(String host, int port) throws IOException {
150 this(host, port, null, STANDARD_SOCKET);
151 }
152
153 /**
154 * Constructor.
155 * Parameter typeSocketA should be either STANDARD_SOCKET, TCP_SOCKET or a
156 * fully qualified classname.
157 *
158 * @param host The host to which the Pdu will be sent
159 * @param port The port where the SNMP server will be
160 * @param typeSocketA The type of socket to use.
161 *
162 * @see SnmpContextBasisFace#STANDARD_SOCKET
163 * @see SnmpContextBasisFace#TCP_SOCKET
164 */
165 protected AbstractSnmpContext(String host, int port, String typeSocketA)
166 throws IOException {
167 this(host, port, null, typeSocketA);
168 }
169
170 /**
171 * Constructor.
172 *
173 * If bindAddress is null, then the system will pick up a valid local
174 * address to bind the socket.
175 *
176 * The typeSocket will indicate which type of socket to use. This way
177 * different handlers can be provided.
178 * It should be either STANDARD_SOCKET, TCP_SOCKET or a
179 * fully qualified classname.
180 *
181 * @param host The host to which the Pdu will be sent
182 * @param port The port where the SNMP server will be
183 * @param bindAddress The local address the server will bind to
184 * @param typeSocketA The type of socket to use.
185 *
186 * @exception IOException Thrown when the socket cannot be
187 * created.
188 *
189 * @see SnmpContextBasisFace#STANDARD_SOCKET
190 * @see SnmpContextBasisFace#TCP_SOCKET
191 * @since 4_14
192 */
193 protected AbstractSnmpContext(String host, int port, String bindAddress, String typeSocketA)
194 throws IOException {
195 pdus = new Pdu[MAXPDU];
196 hostname = host;
197 hostPort = port;
198 bindAddr = bindAddress;
199 typeSocket = typeSocketA;
200 transmitters = new Transmitter[MAXPDU];
201 basename = host + "_" + port + "_" + bindAddress;
202 trapSupport = new TrapReceivedSupport(this);
203 pduSupport = new RequestPduReceivedSupport(this);
204
205 isDestroyed = false;
206 stopRequested = false;
207 maxRecvSize = MSS;
208
209 soc = getSocket(typeSocket);
210 if (soc != null) {
211 soc.create(hostname, hostPort, bindAddr);
212 if (AsnObject.debug > 12) {
213 System.out.println(getClass().getName()
214 + ": soc.getLocalSocketAddress() = "
215 + soc.getLocalSocketAddress());
216 System.out.println(getClass().getName()
217 + ": soc.getRemoteSocketAddress() = "
218 + soc.getRemoteSocketAddress());
219 }
220 }
221 }
222
223 /**
224 * Returns a new socket, based on a particular type.
225 * Parameter type is first compare do STANDARD_SOCKET and TCP_SOCKET. If
226 * that doesn't match, type is assumed to be a fully qualified classname.
227 *
228 * @see SnmpContextBasisFace#STANDARD_SOCKET
229 * @see SnmpContextBasisFace#TCP_SOCKET
230 */
231 static ContextSocketFace getSocket(String type) throws IOException {
232 ContextSocketFace sf = null;
233 if (type != null) {
234 String className = null;
235 if (type.equals(STANDARD_SOCKET)) {
236 className = "uk.co.westhawk.snmp.net.StandardSocket";
237 } else if (type.equals(TCP_SOCKET)) {
238 className = "uk.co.westhawk.snmp.net.TCPSocket";
239 } else {
240 className = type;
241 }
242
243 try {
244 Class cl = Class.forName(className);
245 Object obj = cl.newInstance();
246 sf = (ContextSocketFace) obj;
247 } catch (ClassNotFoundException exc) {
248 String str = "AbstractSnmpContext.getSocket(): ClassNotFound problem " + exc.getMessage() + ", type="
249 + type;
250 throw (new IOException(str));
251 } catch (InstantiationException exc) {
252 String str = "AbstractSnmpContext.getSocket(): Instantiation problem " + exc.getMessage() + ", type="
253 + type;
254 throw (new IOException(str));
255 } catch (IllegalAccessException exc) {
256 String str = "AbstractSnmpContext.getSocket(): IllegalAccess problem " + exc.getMessage() + ", type="
257 + type;
258 throw (new IOException(str));
259 } catch (ClassCastException exc) {
260 String str = "AbstractSnmpContext.getSocket(): ClassCast problem " + exc.getMessage() + ", type="
261 + type;
262 throw (new IOException(str));
263 }
264
265 if (sf == null) {
266 String str = "AbstractSnmpContext.getSocket(): Cannot create socket " + type;
267 throw (new IOException(str));
268 } else {
269 if (AsnObject.debug > 12) {
270 System.out.println("AbstractSnmpContext.getSocket(): New socket " + sf.getClass().getName());
271 }
272 }
273 }
274 return sf;
275 }
276
277 public String getHost() {
278 return hostname;
279 }
280
281 /**
282 * Returns the IP address string
283 * aaa.bbb.ccc.ddd (IPv4) or a:b:c:d:e:f:g:h (IPv6)
284 * of the host.
285 *
286 * @return The IP address of the host
287 * @deprecated As of 4_14, use {@link #getSendToHostAddress()}
288 */
289 public String getHostAddress() {
290 return getSendToHostAddress();
291 }
292
293 public String getSendToHostAddress() {
294 String res = "";
295 if (soc != null) {
296 res = soc.getSendToHostAddress();
297 }
298 return res;
299 }
300
301 public String getReceivedFromHostAddress() {
302 String res = "";
303 if (soc != null) {
304 res = soc.getReceivedFromHostAddress();
305 }
306 return res;
307 }
308
309 public int getPort() {
310 return hostPort;
311 }
312
313 public String getBindAddress() {
314 return bindAddr;
315 }
316
317 public String getTypeSocket() {
318 return typeSocket;
319 }
320
321 /**
322 * Returns the maximum number of bytes this context will read from the
323 * socket. By default this will be set to <code>MSS</code> (i.e. 1300).
324 *
325 * @since 4_12
326 * @see SnmpContextBasisFace#MSS
327 * @see #setMaxRecvSize(int)
328 * @return The number
329 */
330 public int getMaxRecvSize() {
331 return maxRecvSize;
332 }
333
334 /**
335 * Sets the maximum number of bytes this context will read from the
336 * socket. By default this will be set to <code>MSS</code> (i.e. 1300).
337 * The default size seems a reasonable size. The problem usually occurs
338 * when sending Bulk requests.
339 *
340 * <p>
341 * If a packet arrives that is bigger than the maximum size of received
342 * bytes, the stack will try to decode it nevertheless. The usual
343 * error that will occur is:
344 * </p>
345 *
346 * <pre>
347 * Error message: "Incorrect packet. No of bytes received less than packet length."
348 * </pre>
349 *
350 * <p>
351 * Although UDP datagrams can be fragmented (fragmentation is part of
352 * the network layer (IP), not the transport layer (UDP/TCP)), some
353 * firewalls reject incoming fragments. Therefor it is best not to set
354 * maxRecvSize higher than the largest packet size you can get through
355 * your network topology.
356 * </p>
357 *
358 * <p>
359 * Thanks to Pete Kazmier (pete@kazmier.com) for the suggestion.
360 * </p>
361 *
362 * <em style="color:red;">
363 * Note, this property is NOT supported in any of the SNMPContextXXPool
364 * classes.
365 * </em>
366 *
367 * @since 4_12
368 * @see SnmpContextBasisFace#MSS
369 * @see #getMaxRecvSize()
370 * @param no The new number
371 */
372 public void setMaxRecvSize(int no) {
373 maxRecvSize = no;
374 }
375
376 /**
377 * Returns the thread usage of the AbstractSnmpContext.
378 * It returns a String in the form of <code>=PO=QR--------------0</code>.
379 *
380 * <p>
381 * The String represents the array of transmitters.
382 * Each character represents a transmitter slot.
383 * The transmitters form a thread pool of a maximum size, MAXPDU.
384 * Each transmitter is used to wait for one PDU response at a given
385 * moment in time.
386 * When the response is received the transmitter will stop running, but
387 * is not destroyed. It will be reused.
388 * </p>
389 *
390 * <p>
391 * Meaning of each character:
392 * </p>
393 * <ul>
394 * <li><code>-</code> a transmitter slot has not yet been allocated a
395 * thread</li>
396 * <li><code>=</code> there is a thread but it is idle</li>
397 * <li><code>A->Z</code> the thread is transmitting a Pdu</li>
398 * <li>
399 * The last character represents the context's recv thread:
400 * <ul>
401 * <li><code>0</code> there isn't one</li>
402 * <li><code>1</code> it exists but isn't running</li>
403 * <li><code>2</code> it exists and is alive.</li>
404 * </ul>
405 * </li>
406 * </ul>
407 *
408 * @since 4_12
409 * @return The thread usage of the AbstractSnmpContext
410 */
411 public String getDebugString() {
412 char[] cret = new char[MAXPDU + 1];
413 for (int i = 0; i < MAXPDU; i++) {
414 if (transmitters[i] != null) {
415 if (pdus[i] != null) {
416 cret[i] = (char) ('A' + (pdus[i].getReqId() % 26));
417 } else {
418 cret[i] = '=';
419 }
420 } else {
421 cret[i] = '-';
422 }
423 }
424
425 char res = '0';
426 if (me != null) {
427 res++;
428 if (me.isAlive()) {
429 res++;
430 }
431 }
432 cret[MAXPDU] = res;
433
434 return new String(cret);
435 }
436
437 /**
438 * This method will stop the thread.
439 * All transmitters, PDUs in flight and traplisteners will be removed
440 * when run() finishes.
441 *
442 * <p>
443 * It closes the socket.
444 * The thread will actually stop/finish when the run() finishes. Since
445 * the socket is closed, the run() will fall through almost instantly.
446 * </p>
447 *
448 * <p>
449 * Note: The thread(s) will not die immediately; this will take about
450 * half a minute.
451 * </p>
452 *
453 * @see ListeningContext#destroy()
454 * @see ListeningContextPool#destroyPool()
455 */
456 public synchronized void destroy() {
457 if (isDestroyed == false) {
458 stopRequested = true;
459 if (AsnObject.debug > 12) {
460 System.out.println(getClass().getName() + ".destroy(): Closing socket ");
461 }
462 soc.close();
463 isDestroyed = true;
464
465 // If run() has been started, then it will destroy the
466 // transmitter threads when it finishes. Otherwise they must be
467 // destroyed here.
468 if (me == null) {
469 freeTransmitters();
470 }
471 }
472 }
473
474 public boolean isDestroyed() {
475 return isDestroyed;
476 }
477
478 /**
479 * This method will stop the thread.
480 * All transmitters, PDUs in flight and traplisteners will be removed
481 * when run() finishes.
482 * <p>
483 * It does NOT close the socket.
484 * The thread will actually stop/finish when the run() finishes. That is when
485 * a packet arrives on the socket or when the socket times out.
486 * </p>
487 *
488 * <p>
489 * We have deprecated this method since there is no point in stopping
490 * the context, but not destroying it. The context cannot start again anyway.
491 * The difference between destroy() and stop() was not very clear.
492 * </p>
493 *
494 * @deprecated As of version 4_12, should use {@link #destroy()}
495 * @see #destroy()
496 */
497 public synchronized void stop() {
498 stopRequested = true;
499 }
500
501 /**
502 * We wait for any incoming packets. After receiving one, decode
503 * the packet into an Pdu. The Pdu will notify the observers waiting
504 * for an response.
505 *
506 * <p>
507 * Thanks to Chris Barlock <barlock@us.ibm.com> who reported a
508 * NullPointerException in run() on variable 'me' and introduced the
509 * variable stopRequested.
510 * </p>
511 */
512 public void run() {
513 // while It is visible
514 while (!stopRequested) {
515 // block for incoming packets
516 me.yield();
517 try {
518 if (stopRequested) {
519 break;
520 }
521
522 StreamPortItem item = soc.receive(maxRecvSize);
523 ByteArrayInputStream in = item.getStream();
524
525 if (AsnObject.debug > 10) {
526 int nb = in.available();
527 byte[] bu = new byte[nb];
528 in.read(bu);
529 in.reset();
530
531 SnmpUtilities.dumpBytes(getClass().getName()
532 + ".run(): Received from "
533 + item.getHostAddress()
534 + ", from port " + item.getHostPort()
535 + ": ", bu);
536 }
537 processIncomingResponse(in);
538 } catch (IOException exc) {
539 if (exc instanceof InterruptedIOException) {
540 if (AsnObject.debug > 15) {
541 System.out.println(getClass().getName() + ".run(): Idle recv " + exc.getMessage());
542 }
543 } else if (exc instanceof java.net.SocketException) {
544 if (AsnObject.debug > 15) {
545 System.out.println(getClass().getName() + ".run(): SocketException " + exc.getMessage());
546 }
547 } else {
548 if (AsnObject.debug > 0) {
549 System.out.println(getClass().getName() + ".run(): "
550 + exc.getClass().getName() + " " + exc.getMessage());
551 exc.printStackTrace();
552 }
553 }
554 } catch (DecodingException exc) {
555 if (AsnObject.debug > 1) {
556 System.out.println(getClass().getName() + ".run(): DecodingException: " + exc.getMessage());
557 }
558 } catch (Exception exc) {
559 if (AsnObject.debug > 1) {
560 System.out.println(getClass().getName() + ".run(): Exception: " + exc.getMessage());
561 exc.printStackTrace();
562 }
563 } catch (Error err) {
564 if (AsnObject.debug > 1) {
565 System.out.println(getClass().getName() + ".run(): Error: " + err.getMessage());
566 err.printStackTrace();
567 }
568 }
569 }
570
571 freeTransmitters();
572
573 trapSupport.empty();
574 pduSupport.empty();
575
576 // This used to actually create a listener. I do think this bug
577 // has been fixed, since no socket will be created in
578 // ListeningContextPool, unless a listener has been added.
579 ListeningContextPool lcontext = new ListeningContextPool(ListeningContextFace.DEFAULT_TRAP_PORT, bindAddr,
580 typeSocket);
581 lcontext.removeRawPduListenerFromPool(this);
582
583 me = null;
584 soc = null;
585 }
586
587 /*
588 * By moving activate() from the constructor to here, the parameter
589 * maxRecvSize, changed in setMaxRecvSize(), gets a chance to actually
590 * change before run() starts.
591 * Thanks to Dave Hunt <dave.hunt@csipros.com> who reported this
592 * problem.
593 */
594 public synchronized void sendPacket(byte[] p) {
595 if (isDestroyed == false) {
596 activate();
597 try {
598 if (AsnObject.debug > 10) {
599 SnmpUtilities.dumpBytes("Sending to "
600 + soc.getSendToHostAddress() + ": ", p);
601 }
602
603 // Seen it throw an "java.io.IOException: Invalid argument"
604 // when the bind address was wrong, i.e. the packet reach
605 // the host over the interface
606 soc.send(p);
607 } catch (IOException exc) {
608 if (AsnObject.debug > 0) {
609 System.out.println(getClass().getName() + ".sendPacket(): "
610 + exc.getClass().getName()
611 + " " + exc.getMessage());
612 exc.printStackTrace();
613 }
614 }
615 }
616 }
617
618 Pdu getPdu(Integer ReqId) {
619 return getPdu(ReqId.intValue());
620 }
621
622 Pdu getPdu(int rid) {
623 Pdu ret = null;
624 for (int i = 0; i < MAXPDU; i++) {
625 if ((pdus[i] != null) && (pdus[i].getReqId() == rid)) {
626 ret = pdus[i];
627 break;
628 }
629 }
630 return ret;
631 }
632
633 public synchronized boolean removePdu(int rid) {
634 boolean ret = false;
635 for (int i = 0; i < MAXPDU; i++) {
636 if ((pdus[i] != null) && (pdus[i].getReqId() == rid)) {
637 pdus[i] = null;
638 ret = true;
639 break;
640 }
641 }
642 return ret;
643 }
644
645 public synchronized boolean addPdu(Pdu p)
646 throws IOException, PduException {
647 boolean done = false;
648 if (isDestroyed == true) {
649 throw new EncodingException("Context can no longer be used, since it is already destroyed");
650 } else {
651 // I only want to start the receive thread when any of the
652 // context's PDUs is actually expecting a response. See activate().
653 if (anyPduExpectingResponse == false) {
654 anyPduExpectingResponse = p.isExpectingResponse();
655 }
656 for (int i = 0; i < MAXPDU; i++) {
657 if (pdus[i] == null) {
658 pdus[i] = p;
659 pdus[i].setTrans(getTrans(i));
660 done = true;
661 break;
662 }
663 }
664 }
665 return done;
666 }
667
668 public void addTrapListener(TrapListener l) throws IOException {
669 addTrapListener(l, ListeningContextFace.DEFAULT_TRAP_PORT);
670 }
671
672 public void removeTrapListener(TrapListener l) throws IOException {
673 removeTrapListener(l, ListeningContextFace.DEFAULT_TRAP_PORT);
674 }
675
676 public void addTrapListener(TrapListener l, int port)
677 throws IOException {
678 ListeningContextPool lcontext = new ListeningContextPool(port, bindAddr, typeSocket);
679 addTrapListener(l, lcontext);
680 }
681
682 public void removeTrapListener(TrapListener l, int port)
683 throws IOException {
684 ListeningContextPool lcontext = new ListeningContextPool(port, bindAddr, typeSocket);
685 removeTrapListener(l, lcontext);
686 }
687
688 public void addTrapListener(TrapListener l, ListeningContextPool lcontext)
689 throws IOException {
690 trapSupport.addTrapListener(l);
691 lcontext.addRawPduListener(this);
692 }
693
694 public void removeTrapListener(TrapListener l, ListeningContextPool lcontext)
695 throws IOException {
696 trapSupport.removeTrapListener(l);
697 if (trapSupport.getListenerCount() == 0
698 &&
699 pduSupport.getListenerCount() == 0) {
700 lcontext.removeRawPduListener(this);
701 }
702 }
703
704 public void addRequestPduListener(RequestPduListener l)
705 throws IOException {
706 addRequestPduListener(l, SnmpContextBasisFace.DEFAULT_PORT);
707 }
708
709 public void removeRequestPduListener(RequestPduListener l)
710 throws IOException {
711 removeRequestPduListener(l, SnmpContextBasisFace.DEFAULT_PORT);
712 }
713
714 public void addRequestPduListener(RequestPduListener l, int port) throws IOException {
715 ListeningContextPool lcontext = new ListeningContextPool(port, bindAddr, typeSocket);
716 addRequestPduListener(l, lcontext);
717 }
718
719 public void removeRequestPduListener(RequestPduListener l, int port) throws IOException {
720 ListeningContextPool lcontext = new ListeningContextPool(port, bindAddr, typeSocket);
721 removeRequestPduListener(l, lcontext);
722 }
723
724 public void addRequestPduListener(RequestPduListener l, ListeningContextPool lcontext) throws IOException {
725 pduSupport.addRequestPduListener(l);
726 lcontext.addRawPduListener(this);
727 }
728
729 public void removeRequestPduListener(RequestPduListener l, ListeningContextPool lcontext) throws IOException {
730 pduSupport.removeRequestPduListener(l);
731 if (trapSupport.getListenerCount() == 0
732 &&
733 pduSupport.getListenerCount() == 0) {
734 lcontext.removeRawPduListener(this);
735 }
736 }
737
738 /**
739 * Invoked when an undecoded pdu is received.
740 * First the version and the hostaddress are checked, if correct
741 * an attempt is made to decode the pdu.
742 * When successful the original event is consumed and a decoded pdu event
743 * is passed on the listeners.
744 *
745 * @see RawPduReceivedSupport#fireRawPduReceived
746 * @see RequestPduReceivedSupport#fireRequestPduReceived
747 * @see TrapReceivedSupport#fireTrapReceived
748 */
749 public void rawPduReceived(RawPduEvent evt) {
750 String hostAddress = evt.getHostAddress();
751 int version = evt.getVersion();
752 if (version == this.getVersion()) {
753 if (hostAddress != null && hostAddress.equals(this.getSendToHostAddress()) == true) {
754 byte[] message = evt.getMessage();
755 Pdu pdu = null;
756 try {
757 pdu = processIncomingPdu(message);
758 if (pdu != null) {
759 evt.consume();
760 int port = evt.getHostPort();
761
762 if (pdu.getMsgType() == SnmpConstants.TRP_REQ_MSG
763 ||
764 pdu.getMsgType() == SnmpConstants.TRPV2_REQ_MSG) {
765 trapSupport.fireTrapReceived(pdu, port);
766 } else {
767 pduSupport.fireRequestPduReceived(pdu, port);
768 }
769 } else {
770 // somehow the context matches, but the pdu type is
771 // not recognised.
772 }
773 } catch (DecodingException exc) {
774 if (AsnObject.debug > 2) {
775 System.out.println(
776 getClass().getName() + ".rawPduReceived(): DecodingException: " + exc.getMessage());
777 }
778 } catch (IOException exc) {
779 if (AsnObject.debug > 0) {
780 System.out.println(getClass().getName() + ".rawPduReceived(): IOException " + exc.getMessage());
781 }
782 }
783
784 } else {
785 if (AsnObject.debug > 5) {
786 System.out.println(getClass().getName() + ".rawPduReceived(): "
787 + "Pdu host (" + hostAddress
788 + "), does not correspond with context host ("
789 + this.getSendToHostAddress() + ")");
790 }
791 }
792 } else {
793 if (AsnObject.debug > 5) {
794 String theirs = SnmpUtilities.getSnmpVersionString(version);
795 String ours = SnmpUtilities.getSnmpVersionString(this.getVersion());
796 System.out.println(getClass().getName() + ".rawPduReceived(): "
797 + "Pdu version " + theirs
798 + ", does not correspond with context version "
799 + ours);
800 }
801 }
802 }
803
804 Transmitter getTrans(int i) {
805 if (transmitters[i] == null) {
806 transmitters[i] = new Transmitter(basename + "_v" + getVersion() + "_Trans" + i);
807 }
808 return transmitters[i];
809 }
810
811 /**
812 * Creates and starts the Receive thread that allows this context to
813 * receive packets.
814 * Subclasses may override this to adjust the threading behaviour.
815 *
816 * @see PassiveSnmpContext#activate()
817 * @see PassiveSnmpContextv2c#activate()
818 */
819 protected void activate() {
820 // Only start the thread when 'me' is null (i.e. no thread is running)
821 // AND when anyPduExpectingResponse is true.
822 // This way a context that only sends (for example) traps, will not
823 // start a listing thread.
824 if (me == null && anyPduExpectingResponse == true) {
825 me = new Thread(this, basename + "_v" + getVersion() + "_Receive");
826 me.setPriority(me.MAX_PRIORITY);
827 me.start();
828 }
829 }
830
831 /**
832 * Frees the transmitters.
833 *
834 * @see #run()
835 * @see #destroy()
836 * @since 5_1
837 */
838 // In version 5_0, this code lived in run().
839 // Thanks to Vincent Deconinck <vdeconinck@tiscalinet.be>
840 protected void freeTransmitters() {
841 for (int i = 0; i < MAXPDU; i++) {
842 if (transmitters[i] != null) {
843 transmitters[i].destroy();
844 transmitters[i] = null;
845 }
846 if (pdus[i] != null) {
847 pdus[i] = null;
848 }
849 }
850 }
851
852 /**
853 * Returns a clone of this SnmpContext.
854 *
855 * @since 4_14
856 * @exception CloneNotSupportedException Thrown when the constructor
857 * generates an IOException or when in one
858 * of the Pool classes.
859 */
860 public abstract Object clone() throws CloneNotSupportedException;
861
862 /**
863 * Returns the hash key. This key is built out of all properties.
864 *
865 * @since 4_14
866 * @return The hash key
867 */
868 public abstract String getHashKey();
869
870 }