View Javadoc
1   // NAME
2   //      $RCSfile: SnmpContextv3Pool.java,v $
3   // DESCRIPTION
4   //      [given below in javadoc format]
5   // DELTA
6   //      $Revision: 3.27 $
7   // CREATED
8   //      $Date: 2009/03/05 13:27:41 $
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 uk.co.westhawk.snmp.pdu.*;
52  import uk.co.westhawk.snmp.util.*;
53  import uk.co.westhawk.snmp.event.*;
54  import java.util.*;
55  
56  /**
57   * This class contains the pool of SNMP v3 contexts.
58   * This class reuses the existings contexts instead of creating a new
59   * one every time.
60   * <p>
61   * Every time a property changes the pool is checked for a SnmpContextv3
62   * context that matches all the new properties of this class. If no such
63   * context exists, a new one is made.
64   * The PDUs associated with the old context remain associated with the
65   * old context.
66   * </p>
67   *
68   * <p>
69   * A counter indicates the number of times the context is referenced.
70   * The counter is decreased when <code>destroy()</code> is called.
71   * When the counter
72   * reaches zero, the context is released.
73   * </p>
74   *
75   * <p>
76   * Note that because the underlying context can change when a property
77   * is changed and the PDUs remain associated with the old context, all
78   * properties have to be set BEFORE a PDU is sent.
79   * </p>
80   *
81   * @see SnmpContextv3
82   * @see SnmpContextPool
83   * @see SnmpContextv2cPool
84   *
85   * @author <a href="mailto:snmp@westhawk.co.uk">Birgit Arkesteijn</a>
86   * @version $Revision: 3.27 $ $Date: 2009/03/05 13:27:41 $
87   */
88  public class SnmpContextv3Pool implements SnmpContextv3Face {
89      private static final String version_id = "@(#)$Id: SnmpContextv3Pool.java,v 3.27 2009/03/05 13:27:41 birgita Exp $ Copyright Westhawk Ltd";
90  
91      protected static Hashtable contextPool;
92  
93      protected String hostname, socketType, bindAddr;
94      protected int hostPort;
95  
96      protected SnmpContextv3 context = null;
97      protected String userName = "";
98      protected boolean useAuthentication = false;
99      protected String userAuthenticationPassword = "";
100     protected boolean usePrivacy = false;
101     protected String userPrivacyPassword = "";
102     protected int authenticationProtocol = MD5_PROTOCOL;
103     protected byte[] contextEngineId = new byte[0];
104     protected String contextName = DEFAULT_CONTEXT_NAME;
105     protected UsmAgent usmAgent = null;
106 
107     protected boolean hasChanged = false;
108     protected int privacyProtocol = DES_ENCRYPT;
109 
110     /**
111      * Constructor, using the Standard socket.
112      *
113      * @param host The host to which the PDU will be sent
114      * @param port The port where the SNMP server will be
115      * @see SnmpContextv3#SnmpContextv3(String, int)
116      */
117     public SnmpContextv3Pool(String host, int port) throws java.io.IOException {
118         this(host, port, null, STANDARD_SOCKET);
119     }
120 
121     /**
122      * Constructor.
123      * Parameter typeSocket should be either STANDARD_SOCKET, TCP_SOCKET or a
124      * fully qualified classname.
125      *
126      * @param host       The host to which the PDU will be sent
127      * @param port       The port where the SNMP server will be
128      * @param typeSocket The type of socket to use.
129      *
130      * @see SnmpContextv3#SnmpContextv3(String, int, String)
131      * @see SnmpContextBasisFace#STANDARD_SOCKET
132      * @see SnmpContextBasisFace#TCP_SOCKET
133      */
134     public SnmpContextv3Pool(String host, int port, String typeSocket)
135             throws java.io.IOException {
136         this(host, port, null, typeSocket);
137     }
138 
139     /**
140      * Constructor.
141      * Parameter typeSocket should be either STANDARD_SOCKET, TCP_SOCKET or a
142      * fully qualified classname.
143      *
144      * @param host        The host to which the PDU will be sent
145      * @param port        The port where the SNMP server will be
146      * @param bindAddress The local address the server will bind to
147      * @param typeSocket  The type of socket to use.
148      *
149      * @see SnmpContextv3#SnmpContextv3(String, int, String)
150      * @see SnmpContextBasisFace#STANDARD_SOCKET
151      * @see SnmpContextBasisFace#TCP_SOCKET
152      *
153      * @since 4_14
154      */
155     public SnmpContextv3Pool(String host, int port, String bindAddress, String typeSocket)
156             throws java.io.IOException {
157         initPools();
158         hostname = host;
159         hostPort = port;
160         bindAddr = bindAddress;
161         socketType = typeSocket;
162 
163         // No point in creating a context, a lot of the parameters
164         // are probably going to be set.
165         // context = getMatchingContext();
166     }
167 
168     private static synchronized void initPools() {
169         if (contextPool == null) {
170             contextPool = new Hashtable(5);
171         }
172     }
173 
174     /**
175      * Returns the SNMP version of the context.
176      *
177      * @return The version
178      */
179     public int getVersion() {
180         return SnmpConstants.SNMP_VERSION_3;
181     }
182 
183     /**
184      * Returns the host.
185      *
186      * @return The host
187      */
188     public String getHost() {
189         return hostname;
190     }
191 
192     /**
193      * Returns the port number.
194      *
195      * @return The port no
196      */
197     public int getPort() {
198         return hostPort;
199     }
200 
201     public String getBindAddress() {
202         return bindAddr;
203     }
204 
205     /**
206      * Returns the type of socket.
207      *
208      * @return The type of socket
209      */
210     public String getTypeSocket() {
211         return socketType;
212     }
213 
214     public String getSendToHostAddress() {
215         String res = null;
216         if (context != null) {
217             res = context.getSendToHostAddress();
218         }
219         return res;
220     }
221 
222     public String getReceivedFromHostAddress() {
223         String res = null;
224         if (context != null) {
225             res = context.getReceivedFromHostAddress();
226         }
227         return res;
228     }
229 
230     public String getUserName() {
231         return userName;
232     }
233 
234     public void setUserName(String newUserName) {
235         if (newUserName != null && newUserName.equals(userName) == false) {
236             userName = newUserName;
237             hasChanged = true;
238         }
239     }
240 
241     public boolean isUseAuthentication() {
242         return useAuthentication;
243     }
244 
245     public void setUseAuthentication(boolean newUseAuthentication) {
246         if (newUseAuthentication != useAuthentication) {
247             useAuthentication = newUseAuthentication;
248             hasChanged = true;
249         }
250     }
251 
252     public String getUserAuthenticationPassword() {
253         return userAuthenticationPassword;
254     }
255 
256     public void setUserAuthenticationPassword(String newUserAuthenticationPd) {
257         if (newUserAuthenticationPd != null
258                 &&
259                 newUserAuthenticationPd.equals(userAuthenticationPassword) == false) {
260             userAuthenticationPassword = newUserAuthenticationPd;
261             hasChanged = true;
262         }
263     }
264 
265     public void setPrivacyProtocol(int protocol) throws IllegalArgumentException {
266         if (PRIVACY_PROTOCOLS.contains(protocol)) {
267             if (protocol != privacyProtocol) {
268                 privacyProtocol = protocol;
269                 hasChanged = true;
270             }
271         } else {
272             hasChanged = false;
273             throw new IllegalArgumentException("Privacy Protocol "
274                     + "should be DES, AES, AES192, or AES256.");
275         }
276     }
277 
278     public void setAuthenticationProtocol(int protocol)
279             throws IllegalArgumentException {
280         if (AUTH_PROTOCOLS.contains(protocol)) {
281             if (protocol != authenticationProtocol) {
282                 authenticationProtocol = protocol;
283                 hasChanged = true;
284             }
285         } else {
286             hasChanged = false;
287             throw new IllegalArgumentException("Authentication Protocol "
288                     + "should be MD5 or SHA1 or SHA256 or SHA384 or SHA512");
289         }
290     }
291 
292     public int getPrivacyProtocol() {
293         return privacyProtocol;
294     }
295 
296     public int getAuthenticationProtocol() {
297         return authenticationProtocol;
298     }
299 
300     public boolean isUsePrivacy() {
301         return usePrivacy;
302     }
303 
304     public void setUsePrivacy(boolean newUsePrivacy) {
305         if (newUsePrivacy != usePrivacy) {
306             usePrivacy = newUsePrivacy;
307             hasChanged = true;
308         }
309     }
310 
311     public String getUserPrivacyPassword() {
312         return userPrivacyPassword;
313     }
314 
315     public void setUserPrivacyPassword(String newUserPrivacyPd) {
316         if (newUserPrivacyPd != null
317                 &&
318                 newUserPrivacyPd.equals(userPrivacyPassword) == false) {
319             userPrivacyPassword = newUserPrivacyPd;
320             hasChanged = true;
321         }
322     }
323 
324     public void setContextEngineId(byte[] newContextEngineId)
325             throws IllegalArgumentException {
326         if (newContextEngineId != null) {
327             if (newContextEngineId.equals(contextEngineId) == false) {
328                 contextEngineId = newContextEngineId;
329                 hasChanged = true;
330             }
331         } else {
332             hasChanged = false;
333             throw new IllegalArgumentException("contextEngineId is null");
334         }
335     }
336 
337     public byte[] getContextEngineId() {
338         return contextEngineId;
339     }
340 
341     public void setContextName(String newContextName) {
342         if (newContextName != null
343                 &&
344                 newContextName.equals(contextName) == false) {
345             contextName = newContextName;
346             hasChanged = true;
347         }
348     }
349 
350     public String getContextName() {
351         return contextName;
352     }
353 
354     public void setUsmAgent(UsmAgent newAgent) {
355         if (newAgent != null
356                 &&
357                 newAgent != usmAgent) {
358             usmAgent = newAgent;
359             hasChanged = true;
360         }
361     }
362 
363     public UsmAgent getUsmAgent() {
364         return usmAgent;
365     }
366 
367     public boolean addDiscoveryPdu(DiscoveryPdu pdu)
368             throws java.io.IOException, PduException, IllegalArgumentException {
369         if (hasChanged == true || context == null) {
370             context = getMatchingContext();
371         }
372         return context.addDiscoveryPdu(pdu);
373     }
374 
375     public boolean addPdu(Pdu pdu)
376             throws java.io.IOException, PduException, IllegalArgumentException {
377         if (hasChanged == true || context == null) {
378             context = getMatchingContext();
379         }
380         return context.addPdu(pdu);
381     }
382 
383     public boolean removePdu(int requestId) {
384         boolean res = false;
385         if (context != null) {
386             res = context.removePdu(requestId);
387         }
388         return res;
389     }
390 
391     public byte[] encodeDiscoveryPacket(byte msg_type, int rId, int errstat,
392             int errind, Enumeration ve, Object obj)
393             throws java.io.IOException, EncodingException {
394         byte[] res = null;
395         if (context != null) {
396             res = context.encodeDiscoveryPacket(msg_type, rId, errstat, errind, ve, obj);
397         }
398         return res;
399     }
400 
401     public byte[] encodePacket(byte msg_type, int rId, int errstat,
402             int errind, Enumeration ve, Object obj)
403             throws java.io.IOException, EncodingException {
404         byte[] res = null;
405         if (context != null) {
406             res = context.encodePacket(msg_type, rId, errstat, errind, ve,
407                     obj);
408         }
409         return res;
410     }
411 
412     public void sendPacket(byte[] packet) {
413         if (context != null) {
414             context.sendPacket(packet);
415         }
416     }
417 
418     /**
419      * Releases the resources held by this context. This method will
420      * decrement the reference counter. When the reference counter reaches
421      * zero the actual context is removed from the pool and destroyed.
422      */
423     public void destroy() {
424         synchronized (contextPool) {
425             if (context != null) {
426                 String hashKey = context.getHashKey();
427 
428                 int count = 0;
429                 SnmpContextPoolItem item = (SnmpContextPoolItem) contextPool.get(hashKey);
430                 if (item != null) {
431                     count = item.getCounter();
432                     count--;
433                     item.setCounter(count);
434                 }
435 
436                 if (count <= 0) {
437                     contextPool.remove(hashKey);
438                     context.destroy();
439                 }
440                 context = null;
441             }
442         }
443     }
444 
445     /**
446      * Destroys all the contexts in the pool and empties the pool.
447      *
448      * @see #destroy()
449      * @since 4_14
450      */
451     public void destroyPool() {
452         Hashtable copyOfPool = null;
453 
454         synchronized (contextPool) {
455             synchronized (contextPool) {
456                 copyOfPool = (Hashtable) contextPool.clone();
457             }
458             contextPool.clear();
459         }
460         context = null;
461         hasChanged = true;
462 
463         Enumeration keys = copyOfPool.keys();
464         while (keys.hasMoreElements()) {
465             String key = (String) keys.nextElement();
466             SnmpContextPoolItem item = (SnmpContextPoolItem) copyOfPool.get(key);
467             if (item != null) {
468                 SnmpContextBasisFace cntxt = (SnmpContextBasisFace) item.getContext();
469                 cntxt.destroy();
470             }
471         }
472         copyOfPool.clear();
473     }
474 
475     public boolean isDestroyed() {
476         boolean isDestroyed = true;
477         if (context != null) {
478             isDestroyed = context.isDestroyed();
479         }
480         return isDestroyed;
481     }
482 
483     /**
484      * Returns a context from the pool.
485      * The pre-existing context (if there is any) is destroyed.
486      * This methods checks for an existing context that matches all our
487      * properties. If such a context does not exist, a new one is created and
488      * added to the pool.
489      *
490      * @return A context from the pool
491      * @see #getHashKey
492      */
493     protected SnmpContextv3 getMatchingContext()
494             throws java.io.IOException, IllegalArgumentException {
495         SnmpContextPoolItem item = null;
496         SnmpContextv3 newContext = null;
497         String hashKey = getHashKey();
498 
499         destroy();
500         synchronized (contextPool) {
501             int count = 0;
502             if (contextPool.containsKey(hashKey)) {
503                 item = (SnmpContextPoolItem) contextPool.get(hashKey);
504                 newContext = (SnmpContextv3) item.getContext();
505                 count = item.getCounter();
506             } else {
507                 newContext = new SnmpContextv3(hostname, hostPort, bindAddr, socketType);
508                 newContext.setContextEngineId(contextEngineId);
509                 newContext.setContextName(contextName);
510                 newContext.setUserName(userName);
511                 newContext.setUseAuthentication(useAuthentication);
512                 newContext.setUserAuthenticationPassword(userAuthenticationPassword);
513                 newContext.setAuthenticationProtocol(authenticationProtocol);
514                 newContext.setUsePrivacy(usePrivacy);
515                 newContext.setUserPrivacyPassword(userPrivacyPassword);
516                 newContext.setUsmAgent(usmAgent);
517                 newContext.setPrivacyProtocol(privacyProtocol);
518 
519                 item = new SnmpContextPoolItem(newContext);
520                 contextPool.put(hashKey, item);
521             }
522             hasChanged = false;
523             count++;
524             item.setCounter(count);
525         }
526         return newContext;
527     }
528 
529     /**
530      * Dumps the pool of contexts. This is for debug purposes.
531      * 
532      * @param title The title of the dump
533      */
534     public void dumpContexts(String title) {
535         try {
536             if (hasChanged == true) {
537                 context = getMatchingContext();
538             }
539         } catch (java.io.IOException exc) {
540             if (AsnObject.debug > 0) {
541                 System.out.println(getClass().getName() + ".dumpContexts(): " + exc.getMessage());
542             }
543         }
544 
545         System.out.println(title + " " + contextPool.size() + " context(s)");
546         Enumeration keys = contextPool.keys();
547         int i = 0;
548         while (keys.hasMoreElements()) {
549             String key = (String) keys.nextElement();
550             SnmpContextPoolItem item = (SnmpContextPoolItem) contextPool.get(key);
551             if (item != null) {
552                 int count = item.getCounter();
553                 SnmpContextv3 cntxt = (SnmpContextv3) item.getContext();
554 
555                 if (cntxt == context) {
556                     System.out.println("\tcurrent context: ");
557                 }
558                 System.out.println("\tcontext " + i + ": " + key + ", count: " + count
559                         + ", " + cntxt.toString() + "\n"
560                         + ", " + cntxt.getDebugString());
561                 i++;
562             }
563         }
564         System.out.println("\thasChanged: " + hasChanged);
565     }
566 
567     /**
568      * Returns the hash key. This key is built out of all properties. It
569      * serves as key for the hashtable of (v3) contexts.
570      *
571      * @return The hash key
572      */
573     public String getHashKey() {
574         StringBuffer buffer = new StringBuffer();
575         buffer.append(hostname);
576         buffer.append("_").append(hostPort);
577         buffer.append("_").append(bindAddr);
578         buffer.append("_").append(socketType);
579         buffer.append("_").append(useAuthentication);
580         buffer.append("_").append(PROTOCOL_NAMES[authenticationProtocol]);
581         buffer.append("_").append(PROTOCOL_NAMES[privacyProtocol]);
582         buffer.append("_").append(userAuthenticationPassword);
583         buffer.append("_").append(userName);
584         buffer.append("_").append(usePrivacy);
585         buffer.append("_").append(userPrivacyPassword);
586         buffer.append("_").append(SnmpUtilities.toHexString(contextEngineId));
587         buffer.append("_").append(contextName);
588         buffer.append("_v").append(getVersion());
589 
590         return buffer.toString();
591     }
592 
593     public void addTrapListener(TrapListener l) throws java.io.IOException {
594         if (hasChanged == true || context == null) {
595             context = getMatchingContext();
596         }
597         context.addTrapListener(l);
598     }
599 
600     public void removeTrapListener(TrapListener l) throws java.io.IOException {
601         if (hasChanged == true || context == null) {
602             context = getMatchingContext();
603         }
604         context.removeTrapListener(l);
605     }
606 
607     public void addTrapListener(TrapListener l, int port) throws java.io.IOException {
608         if (hasChanged == true || context == null) {
609             context = getMatchingContext();
610         }
611         context.addTrapListener(l, port);
612     }
613 
614     public void removeTrapListener(TrapListener l, int port) throws java.io.IOException {
615         if (hasChanged == true || context == null) {
616             context = getMatchingContext();
617         }
618         context.removeTrapListener(l, port);
619     }
620 
621     public void addTrapListener(TrapListener l, ListeningContextPool lcontext) throws java.io.IOException {
622         if (hasChanged == true || context == null) {
623             context = getMatchingContext();
624         }
625         context.addTrapListener(l, lcontext);
626     }
627 
628     public void removeTrapListener(TrapListener l, ListeningContextPool lcontext) throws java.io.IOException {
629         if (hasChanged == true || context == null) {
630             context = getMatchingContext();
631         }
632         context.removeTrapListener(l, lcontext);
633     }
634 
635     public void addRequestPduListener(RequestPduListener l) throws java.io.IOException {
636         if (hasChanged == true || context == null) {
637             context = getMatchingContext();
638         }
639         context.addRequestPduListener(l);
640     }
641 
642     public void removeRequestPduListener(RequestPduListener l) throws java.io.IOException {
643         if (hasChanged == true || context == null) {
644             context = getMatchingContext();
645         }
646         context.removeRequestPduListener(l);
647     }
648 
649     public void addRequestPduListener(RequestPduListener l, int port) throws java.io.IOException {
650         if (hasChanged == true || context == null) {
651             context = getMatchingContext();
652         }
653         context.addRequestPduListener(l, port);
654     }
655 
656     public void removeRequestPduListener(RequestPduListener l, int port) throws java.io.IOException {
657         if (hasChanged == true || context == null) {
658             context = getMatchingContext();
659         }
660         context.removeRequestPduListener(l, port);
661     }
662 
663     public void addRequestPduListener(RequestPduListener l, ListeningContextPool lcontext) throws java.io.IOException {
664         if (hasChanged == true || context == null) {
665             context = getMatchingContext();
666         }
667         context.addRequestPduListener(l, lcontext);
668     }
669 
670     public void removeRequestPduListener(RequestPduListener l, ListeningContextPool lcontext)
671             throws java.io.IOException {
672         if (hasChanged == true || context == null) {
673             context = getMatchingContext();
674         }
675         context.removeRequestPduListener(l, lcontext);
676     }
677 
678     public Pdu processIncomingPdu(byte[] message)
679             throws DecodingException, java.io.IOException {
680         if (hasChanged == true || context == null) {
681             context = getMatchingContext();
682         }
683 
684         Pdu pdu = null;
685         pdu = context.processIncomingPdu(message);
686         return pdu;
687     }
688 
689     /**
690      * Returns a string representation of the object.
691      * 
692      * @return The string
693      */
694     public String toString() {
695         String res = "";
696         try {
697             if (hasChanged == true || context == null) {
698                 context = getMatchingContext();
699             }
700             res = context.toString();
701         } catch (java.io.IOException exc) {
702             if (AsnObject.debug > 0) {
703                 System.out.println(getClass().getName() + ".toString(): " + exc.getMessage());
704             }
705         }
706 
707         return res;
708     }
709 
710     /**
711      * This method is not supported. It will throw a CloneNotSupportedException.
712      *
713      * @since 4_14
714      */
715     public Object clone() throws CloneNotSupportedException {
716         throw new CloneNotSupportedException();
717     }
718 
719 }