View Javadoc
1   // NAME
2   //      $RCSfile: SnmpContextv3Basis.java,v $
3   // DESCRIPTION
4   //      [given below in javadoc format]
5   // DELTA
6   //      $Revision: 3.17 $
7   // CREATED
8   //      $Date: 2009/03/05 15:51:42 $
9   // COPYRIGHT
10  //      Westhawk Ltd
11  // TO DO
12  //
13  
14  /*
15   * Copyright (C) 2005 - 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.beans.UsmDiscoveryBean;
52  import uk.co.westhawk.snmp.event.RequestPduListener;
53  import uk.co.westhawk.snmp.pdu.DiscoveryPdu;
54  import uk.co.westhawk.snmp.util.SnmpUtilities;
55  
56  import java.io.ByteArrayInputStream;
57  import java.io.IOException;
58  import java.util.Enumeration;
59  import java.util.Hashtable;
60  import java.util.function.BiFunction;
61  
62  /**
63   * This class contains the basis for the SNMP v3 contexts that is needed 
64   * by every PDU to send a SNMP v3 request.
65   *
66   * <p>
67   * This class will perform the v3 discovery of the SNMP engine ID and
68   * time line if necessary. This is done with the classes
69   * <code>TimeWindow</code> and <code>UsmDiscoveryBean</code>.
70   * </p>
71   *
72   * <p>
73   * Now that the stack can send traps and receive requests, 
74   * it needs to be able to act as an
75   * authoritative SNMP engine. This is done via the interface UsmAgent.
76   * The DefaultUsmAgent is not guaranteed to work; agents (or rather 
77   * authoritative engines) <em>should</em> provide a better implementation.
78   * </p>
79   *
80   * <p>
81   * This class will use the User Security Model (USM) as described in 
82   * <a href="http://www.ietf.org/rfc/rfc3414.txt">SNMP-USER-BASED-SM-MIB</a>.
83   * See also <a href="http://www.ietf.org/rfc/rfc3411.txt">RFC 3411</a>.
84   * </p>
85   *
86   * <p>
87   * It is advised to set all the properties of this class before any PDU,
88   * using this class, is sent. 
89   * All properties are being used to encode the message. Some properties are 
90   * being used to decode the Response or Report PDU. 
91   * When any of these last properties were changed in between flight there 
92   * is a possibility the decoding fails, causing a
93   * <code>DecodingException</code>. 
94   * </p>
95   * 
96   * <p>
97   * <code>destroy()</code> should be called when the context is no longer
98   * used. This is the only way the threads will be stopped and garbage
99   * collected.
100  * </p>
101  *
102  * @see SnmpContextv3Face
103  * @see SnmpContextv3Pool
104  * @see TimeWindow
105  * @see UsmAgent
106  * @see DefaultUsmAgent
107  * @see #setUsmAgent(UsmAgent)
108  * @see UsmDiscoveryBean
109  *
110  * @since 4_14
111  * @author <a href="mailto:snmp@westhawk.co.uk">Birgit Arkesteijn</a>
112  * @version $Revision: 3.17 $ $Date: 2009/03/05 15:51:42 $
113  */
114 public abstract class SnmpContextv3Basis extends AbstractSnmpContext
115         implements SnmpContextv3Face, Cloneable {
116     private static final String version_id = "@(#)$Id: SnmpContextv3Basis.java,v 3.17 2009/03/05 15:51:42 birgita Exp $ Copyright Westhawk Ltd";
117 
118     public static final int AES128_KEY_LENGTH = 16;
119     public static final int AES192_KEY_LENGTH = 24;
120     public static final int AES256_KEY_LENGTH = 32;
121 
122     protected String userName = DEFAULT_USERNAME;
123     protected boolean useAuthentication = false;
124     protected String userAuthenticationPassword;
125     protected byte[] userAuthKeyMD5 = null;
126     protected byte[] userAuthKeySHA1 = null;
127     protected byte[] userAuthKeySHA256 = null;
128     protected byte[] userAuthKeySHA512 = null;
129     protected byte[] userAuthKeySHA224 = null;
130     protected byte[] userAuthKeySHA384 = null;
131     protected int authenticationProtocol = MD5_PROTOCOL;
132     protected int privacyProtocol = DES_ENCRYPT;
133     protected boolean usePrivacy = false;
134     protected String userPrivacyPassword;
135     protected byte[] userPrivKeyMD5 = null;
136     protected byte[] userPrivKeySHA1 = null;
137     protected byte[] userPrivKeySHA256 = null;
138     protected byte[] userPrivKeySHA512 = null;
139     protected byte[] userPrivKeySHA224 = null;
140     protected byte[] userPrivKeySHA384 = null;
141     protected byte[] contextEngineId = new byte[0];
142     protected String contextName = DEFAULT_CONTEXT_NAME;
143     protected UsmAgent usmAgent = null;
144 
145     private Hashtable msgIdHash = new Hashtable(MAXPDU);
146     private static int next_id = 1;
147 
148     /**
149      * Constructor.
150      *
151      * @param host The host to which the PDU will be sent
152      * @param port The port where the SNMP server will be
153      * @see AbstractSnmpContext#AbstractSnmpContext(String, int)
154      */
155     public SnmpContextv3Basis(String host, int port) throws IOException {
156         this(host, port, null, STANDARD_SOCKET);
157     }
158 
159     /**
160      * Constructor.
161      * Parameter typeSocketA should be either STANDARD_SOCKET, TCP_SOCKET or a
162      * fully qualified classname.
163      *
164      * @param host        The host to which the Pdu will be sent
165      * @param port        The port where the SNMP server will be
166      * @param typeSocketA The local address the server will bind to
167      *
168      * @see AbstractSnmpContext#AbstractSnmpContext(String, int, String)
169      */
170     public SnmpContextv3Basis(String host, int port, String typeSocketA)
171             throws IOException {
172         this(host, port, null, typeSocketA);
173     }
174 
175     /**
176      * Constructor.
177      * Parameter typeSocketA should be either STANDARD_SOCKET, TCP_SOCKET or a
178      * fully qualified classname.
179      *
180      * @param host        The host to which the PDU will be sent
181      * @param port        The port where the SNMP server will be
182      * @param bindAddress The local address the server will bind to
183      * @param typeSocketA The type of socket to use.
184      *
185      * @see AbstractSnmpContext#AbstractSnmpContext(String, int, String)
186      * @see SnmpContextBasisFace#STANDARD_SOCKET
187      * @see SnmpContextBasisFace#TCP_SOCKET
188      * @since 4_14
189      */
190     public SnmpContextv3Basis(String host, int port, String bindAddress, String typeSocketA)
191             throws IOException {
192         super(host, port, bindAddress, typeSocketA);
193 
194         if (TimeWindow.getCurrent() == null) {
195             TimeWindow timew = new TimeWindow();
196         }
197         setUsmAgent(createUsmAgent());
198     }
199 
200     public int getVersion() {
201         return SnmpConstants.SNMP_VERSION_3;
202     }
203 
204     /**
205      * Returns the username.
206      *
207      * @return the username
208      */
209     public String getUserName() {
210         return userName;
211     }
212 
213     /**
214      * Sets the username.
215      * This username will be used for all PDUs sent with this context.
216      * The username corresponds to the 'msgUserName' in
217      * <a href="http://www.ietf.org/rfc/rfc3414.txt">SNMP-USER-BASED-SM-MIB</a>.
218      * The default value is "initial".
219      *
220      * @param newUserName The new username
221      * @see #DEFAULT_USERNAME
222      */
223     public void setUserName(String newUserName) {
224         userName = newUserName;
225     }
226 
227     /**
228      * Returns if authentication is used or not.
229      * By default no authentication will be used.
230      *
231      * @return true if authentication is used, false if not
232      */
233     public boolean isUseAuthentication() {
234         return useAuthentication;
235     }
236 
237     /**
238      * Sets whether authentication has to be used.
239      * By default no authentication will be used.
240      *
241      * @param newUseAuthentication The use of authentication
242      */
243     public void setUseAuthentication(boolean newUseAuthentication) {
244         useAuthentication = newUseAuthentication;
245     }
246 
247     /**
248      * Returns the user authentication password.
249      * This password will be transformed into the user authentication secret key.
250      *
251      * @return The user authentication password
252      */
253     public String getUserAuthenticationPassword() {
254         return userAuthenticationPassword;
255     }
256 
257     /**
258      * Sets the user authentication password.
259      * This password will be transformed into the user authentication secret
260      * key. A user MUST set this password.
261      *
262      * @param newUserAuthPassword The user authentication password
263      */
264     public void setUserAuthenticationPassword(String newUserAuthPassword) {
265         if (newUserAuthPassword != null
266                 &&
267                 newUserAuthPassword.equals(userAuthenticationPassword) == false) {
268             userAuthenticationPassword = newUserAuthPassword;
269             userAuthKeyMD5 = null;
270             userAuthKeySHA1 = null;
271             userAuthKeySHA256 = null;
272             userAuthKeySHA512 = null;
273             userAuthKeySHA224 = null;
274             userAuthKeySHA384 = null;
275         }
276     }
277 
278     /**
279      * Sets the protocol to be used for authentication.
280      * This can either be MD5 or SHA-1.
281      * By default MD5 will be used.
282      *
283      * @param protocol The authentication protocol to be used
284      * @see #MD5_PROTOCOL
285      * @see #SHA1_PROTOCOL
286      */
287     public void setAuthenticationProtocol(int protocol)
288             throws IllegalArgumentException {
289         if (AUTH_PROTOCOLS.contains(protocol)) {
290             if (protocol != authenticationProtocol) {
291                 authenticationProtocol = protocol;
292             }
293         } else {
294             throw new IllegalArgumentException("Authentication Protocol "
295                     + "should be MD5 or SHA1 or SHA256 or SHA512 or SHA224 or SHA384");
296         }
297     }
298 
299     /**
300      * Returns the protocol to be used for authentication.
301      * This can either be MD5 or SHA-1.
302      * By default MD5 will be used.
303      *
304      * @return The authentication protocol to be used
305      * @see #MD5_PROTOCOL
306      * @see #SHA1_PROTOCOL
307      */
308     public int getAuthenticationProtocol() {
309         return authenticationProtocol;
310     }
311 
312     /**
313      * Sets the protocol to be used for privacy.
314      * This can either be DES or AES.
315      * By default DES will be used.
316      *
317      * @param protocol The privacy protocol to be used
318      * @see SnmpContextv3Face#AES_ENCRYPT
319      * @see SnmpContextv3Face#DES_ENCRYPT
320      */
321     public void setPrivacyProtocol(int protocol)
322             throws IllegalArgumentException {
323         if (PRIVACY_PROTOCOLS.contains(protocol)) {
324             if (protocol != privacyProtocol) {
325                 privacyProtocol = protocol;
326             }
327         } else {
328             throw new IllegalArgumentException("Privacy Encryption "
329                     + "should be AES, AES192, AES256 or DES");
330         }
331     }
332 
333     /**
334      * Returns the protocol to be used for privacy.
335      * This can either be DES or AES.
336      * By default DES will be used.
337      *
338      * @return The privacy protocol to be used
339      * @see SnmpContextv3Face#AES_ENCRYPT
340      * @see SnmpContextv3Face#DES_ENCRYPT
341      */
342     public int getPrivacyProtocol() {
343         return privacyProtocol;
344     }
345 
346     byte[] getAuthenticationPasswordKeyMD5() {
347         if (userAuthKeyMD5 == null) {
348             userAuthKeyMD5 = SnmpUtilities.passwordToKeyMD5(userAuthenticationPassword);
349         }
350         return userAuthKeyMD5;
351     }
352 
353     byte[] getAuthenticationPasswordKeySHA1() {
354         if (userAuthKeySHA1 == null) {
355             userAuthKeySHA1 = SnmpUtilities.passwordToKeySHA1(userAuthenticationPassword);
356         }
357         return userAuthKeySHA1;
358     }
359 
360     /**
361      * Returns the authentication password key for SHA256.
362      * 
363      * @return the authentication password key for SHA256
364      */
365     byte[] getAuthenticationPasswordKeySHA256() {
366         if (userAuthKeySHA256 == null) {
367             userAuthKeySHA256 = SnmpUtilities.passwordToKeySHA256(userAuthenticationPassword);
368         }
369         return userAuthKeySHA256;
370     }
371 
372     /**
373      * Returns the authentication password key for SHA-384.
374      *
375      * @return the authentication password key for SHA-384
376      */
377     byte[] getAuthenticationPasswordKeySHA384() {
378         if (userAuthKeySHA384 == null) {
379             userAuthKeySHA384 = SnmpUtilities.passwordToKeySHA384(userAuthenticationPassword);
380         }
381         return userAuthKeySHA384;
382     }
383 
384     /**
385      * Returns the authentication password key for SHA-224.
386      *
387      * @return the authentication password key for SHA-224
388      */
389     byte[] getAuthenticationPasswordKeySHA224() {
390         if (userAuthKeySHA224 == null) {
391             userAuthKeySHA224 = SnmpUtilities.passwordToKeySHA224(userAuthenticationPassword);
392         }
393         return userAuthKeySHA224;
394     }
395 
396     /**
397      * Returns the authentication password key for SHa512.
398      *
399      * @return the authentication password key for SHA512
400      */
401     byte[] getAuthenticationPasswordKeySHA512() {
402         if (userAuthKeySHA512 == null) {
403             userAuthKeySHA512 = SnmpUtilities.passwordToKeySHA512(userAuthenticationPassword);
404         }
405         return userAuthKeySHA512;
406     }
407 
408     byte[] getPrivacyPasswordKeyMD5() {
409         if (userPrivKeyMD5 == null) {
410             userPrivKeyMD5 = SnmpUtilities.passwordToKeyMD5(userPrivacyPassword);
411         }
412         return userPrivKeyMD5;
413     }
414 
415     byte[] getPrivacyPasswordKeySHA1() {
416         if (userPrivKeySHA1 == null) {
417             userPrivKeySHA1 = SnmpUtilities.passwordToKeySHA1(userPrivacyPassword);
418         }
419         return userPrivKeySHA1;
420     }
421 
422     /**
423      * Returns the privacy password key for SHA256.
424      * 
425      * @return the privacy password key for SHA256
426      */
427     byte[] getPrivacyPasswordKeySHA256() {
428         if (userPrivKeySHA256 == null) {
429             userPrivKeySHA256 = SnmpUtilities.passwordToKeySHA256(userPrivacyPassword);
430         }
431         return userPrivKeySHA256;
432     }
433 
434     /**
435      * Returns the privacy password key for SHA-224.
436      *
437      * @return the privacy password key for SHA-224
438      */
439     byte[] getPrivacyPasswordKeySHA224() {
440         if (userPrivKeySHA224 == null) {
441             userPrivKeySHA224 = SnmpUtilities.passwordToKeySHA224(userPrivacyPassword);
442         }
443         return userPrivKeySHA224;
444     }
445 
446     /**
447      * Returns the privacy password key for SHA-384.
448      *
449      * @return the privacy password key for SHA-384
450      */
451     byte[] getPrivacyPasswordKeySHA384() {
452         if (userPrivKeySHA384 == null) {
453             userPrivKeySHA384 = SnmpUtilities.passwordToKeySHA384(userPrivacyPassword);
454         }
455         return userPrivKeySHA384;
456     }
457 
458     /**
459      * Returns the privacy password key for SHA512.
460      *
461      * @return the privacy password key for SHA512
462      */
463     byte[] getPrivacyPasswordKeySHA512() {
464         if (userPrivKeySHA512 == null) {
465             userPrivKeySHA512 = SnmpUtilities.passwordToKeySHA512(userPrivacyPassword);
466         }
467         return userPrivKeySHA512;
468     }
469 
470     /**
471      * Returns if privacy is used or not.
472      * By default privacy is not used.
473      *
474      * @return true if privacy is used, false if not
475      */
476     public boolean isUsePrivacy() {
477         return usePrivacy;
478     }
479 
480     /**
481      * Sets whether privacy has to be used.
482      * By default privacy is not used.
483      * Note, privacy (encryption) without authentication is not allowed.
484      *
485      * @param newUsePrivacy The use of privacy
486      */
487     public void setUsePrivacy(boolean newUsePrivacy) {
488         usePrivacy = newUsePrivacy;
489     }
490 
491     /**
492      * Returns the user privacy password.
493      * This password will be transformed into the user privacy secret key.
494      *
495      * @return The user privacy password
496      */
497     public String getUserPrivacyPassword() {
498         return userPrivacyPassword;
499     }
500 
501     /**
502      * Sets the user privacy password.
503      * This password will be transformed into the user privacy secret
504      * key. A user <em>must</em> set this password in order to use privacy.
505      *
506      * @param newUserPrivacyPassword The user privacy password
507      */
508     public void setUserPrivacyPassword(String newUserPrivacyPassword) {
509         if (newUserPrivacyPassword != null
510                 &&
511                 newUserPrivacyPassword.equals(userPrivacyPassword) == false) {
512             userPrivacyPassword = newUserPrivacyPassword;
513             userPrivKeyMD5 = null;
514             userPrivKeySHA1 = null;
515             userPrivKeySHA256 = null;
516             userPrivKeySHA512 = null;
517             userPrivKeySHA224 = null;
518             userPrivKeySHA384 = null;
519         }
520     }
521 
522     /**
523      * Sets the contextEngineID.
524      * See <a href="http://www.ietf.org/rfc/rfc3411.txt">RFC 3411</a>.
525      *
526      * A contextEngineID uniquely
527      * identifies an SNMP entity that may realize an instance of a context
528      * with a particular contextName.
529      * 
530      * <p>
531      * Note, when the stack is an authoritative engine, this parameter should
532      * equal the UsmAgent.getSnmpEngineId(). See the StackUsage
533      * documentation for an explanation.
534      * </p>
535      *
536      * <p>
537      * If the contextEngineID is of length zero, the encoder will use the
538      * (discovered)
539      * snmpEngineId.
540      * </p>
541      *
542      * @see UsmAgent#getSnmpEngineId()
543      * @param newContextEngineId The contextEngineID
544      */
545     public void setContextEngineId(byte[] newContextEngineId)
546             throws IllegalArgumentException {
547         if (newContextEngineId != null) {
548             contextEngineId = newContextEngineId;
549         } else {
550             throw new IllegalArgumentException("contextEngineId is null");
551         }
552     }
553 
554     /**
555      * Returns the contextEngineID.
556      *
557      * @return The contextEngineID
558      */
559     public byte[] getContextEngineId() {
560         return contextEngineId;
561     }
562 
563     /**
564      * Sets the contextName.
565      * See <a href="http://www.ietf.org/rfc/rfc3411.txt">RFC 3411</a>.
566      *
567      * A contextName is used to name a context. Each contextName MUST be
568      * unique within an SNMP entity.
569      * By default this is "" (the empty String).
570      *
571      * @param newContextName The contextName
572      * @see #DEFAULT_CONTEXT_NAME
573      */
574     public void setContextName(String newContextName) {
575         contextName = newContextName;
576     }
577 
578     /**
579      * Returns the contextName.
580      *
581      * @return The contextName
582      */
583     public String getContextName() {
584         return contextName;
585     }
586 
587     /**
588      * Adds a discovery pdu. This method adds the PDU (without checking if
589      * discovery is needed).
590      *
591      * @param pdu the discovery pdu
592      * @return pdu is succesful added
593      * @see AbstractSnmpContext#addPdu(Pdu)
594      * @see #addPdu(Pdu)
595      */
596     public boolean addDiscoveryPdu(DiscoveryPdu pdu)
597             throws IOException, PduException {
598         // since this is a DiscoveryPdu we do not check for discovery :-)
599         return this.addPdu(pdu, false);
600     }
601 
602     /**
603      * Adds a PDU. This method adds the PDU and blocks until it has all the
604      * discovery parameters it needs.
605      *
606      * @param pdu the PDU
607      * @return pdu is succesful added
608      * @see AbstractSnmpContext#addPdu(Pdu)
609      * @see #addDiscoveryPdu(DiscoveryPdu)
610      */
611     public boolean addPdu(Pdu pdu)
612             throws IOException, PduException {
613         return this.addPdu(pdu, true);
614     }
615 
616     /**
617      * Creates the USM agent.
618      * 
619      * @see DefaultUsmAgent
620      * @see #isAuthoritative
621      */
622     protected UsmAgent createUsmAgent() {
623         return new DefaultUsmAgent();
624     }
625 
626     /**
627      * Sets the UsmAgent, needed when this stack is used as authoritative
628      * SNMP engine. This interface provides authentiation details, like its
629      * clock and its Engine ID.
630      * 
631      * @see DefaultUsmAgent
632      * @param agent The USM authoritative interface
633      * @since 4_14
634      */
635     public void setUsmAgent(UsmAgent agent) {
636         usmAgent = agent;
637     }
638 
639     /**
640      * Returns the UsmAgent.
641      * 
642      * @see #setUsmAgent
643      * @since 4_14
644      */
645     public UsmAgent getUsmAgent() {
646         return usmAgent;
647     }
648 
649     /**
650      * Adds a PDU. This method adds the PDU and checks if discovery is
651      * needed depending on the parameter <code>checkDiscovery</code>.
652      * If discovery is needed this method will block until it has done so.
653      * Discovery is only needed if the stack is non authoritative.
654      *
655      * <p>
656      * This method stores the SNMPv3 msgId and PDU
657      * request id in a Hashtable.
658      * Since the encoding only happens once and every retry sends the same
659      * encoded packet, only one msgId is used.
660      * </p>
661      *
662      * @param pdu            the PDU
663      * @param checkDiscovery check if discovery is needed
664      * @return pdu is succesful added
665      * @see AbstractSnmpContext#addPdu(Pdu)
666      * @see #addDiscoveryPdu(DiscoveryPdu)
667      * @see #addPdu(Pdu)
668      */
669     protected boolean addPdu(Pdu pdu, boolean checkDiscovery)
670             throws IOException, PduException {
671         // TODO, when sending response or report, the msgId should be set!
672         Integer msgId = pdu.snmpv3MsgId;
673         if (msgId == null) {
674             msgId = new Integer(next_id++);
675         } else if (pdu.isExpectingResponse() == true) {
676             // generate a new msgId, even if this is already set. The user
677             // could be adding the same PDU more than once to the
678             // context.
679             msgId = new Integer(next_id++);
680         }
681         pdu.snmpv3MsgId = msgId;
682 
683         msgIdHash.put(msgId, new Integer(pdu.req_id));
684         if (AsnObject.debug > 6) {
685             System.out.println(getClass().getName() + ".addPdu(): msgId="
686                     + msgId.toString() + ", Pdu reqId=" + pdu.req_id);
687         }
688 
689         if (checkDiscovery == true && isAuthoritative(pdu.getMsgType()) == false) {
690             discoverIfNeeded(pdu);
691         }
692 
693         boolean added = super.addPdu(pdu);
694         return added;
695     }
696 
697     /**
698      * Removes a PDU. This removes the PDU from the AbstractSnmpContext and
699      * clears the link with the SNMPv3 msgId.
700      *
701      * @param rid the PDU request id
702      * @return whether the PDU has been successfully removed
703      * @see AbstractSnmpContext#removePdu(int)
704      */
705     public synchronized boolean removePdu(int rid) {
706         boolean removed = super.removePdu(rid);
707         if (removed) {
708             Enumeration keys = msgIdHash.keys();
709             Integer msgIdI = null;
710             boolean found = false;
711             while (keys.hasMoreElements() && found == false) {
712                 msgIdI = (Integer) keys.nextElement();
713                 Integer pduIdI = (Integer) msgIdHash.get(msgIdI);
714                 found = (pduIdI.intValue() == rid);
715             }
716             if (found) {
717                 msgIdHash.remove(msgIdI);
718             }
719         }
720         return removed;
721     }
722 
723     /**
724      * Encodes a discovery PDU packet. This methods encodes without checking
725      * if the discovery parameters are all known.
726      */
727     public byte[] encodeDiscoveryPacket(byte msg_type, int rId, int errstat,
728             int errind, Enumeration ve, Object obj)
729             throws IOException, EncodingException {
730         String engineId = "";
731         TimeWindow tWindow = TimeWindow.getCurrent();
732         if (tWindow.isSnmpEngineIdKnown(getSendToHostAddress(), hostPort) == true) {
733             engineId = tWindow.getSnmpEngineId(getSendToHostAddress(), hostPort);
734         }
735         TimeWindowNode node = new TimeWindowNode(engineId, 0, 0);
736 
737         return actualEncodePacket(msg_type, rId, errstat, errind, ve, node,
738                 obj);
739     }
740 
741     /**
742      * Encodes a PDU. This is for internal use only and should
743      * NOT be called by the developer.
744      * This is called by the the PDU itself and is added to the interface to
745      * cover the different kind of Contexts.
746      *
747      * <p>
748      * When the stack is
749      * </p>
750      * <ul>
751      * <li>
752      * authoritative, the timeline details are retrieved from the UsmAgent.
753      * </li>
754      * <li>
755      * non authoritative, this methods first checks if all the discovery
756      * parameters are known;
757      * <ul>
758      * <li>
759      * If so, it encodes and returns the bytes.
760      * </li>
761      * <li>
762      * If not, it will throw an EncodingException.
763      * </li>
764      * </ul>
765      * </li>
766      * </ul>
767      *
768      * @see #isAuthoritative(byte)
769      * @param msg_type The message type
770      * @param rId      The message id
771      * @param errstat  The error status
772      * @param errind   The error index
773      * @param ve       The varbind list
774      * @param obj      Additional object (only used in SNMPv3)
775      * @return The encoded packet
776      */
777     public byte[] encodePacket(byte msg_type, int rId, int errstat,
778             int errind, Enumeration ve, Object obj)
779             throws IOException, EncodingException {
780         TimeWindowNode node = null;
781         if (isDestroyed == true) {
782             throw new EncodingException("Context can no longer be used, since it is already destroyed");
783         } else {
784             TimeWindow tWindow = TimeWindow.getCurrent();
785             if (isAuthoritative(msg_type) == true) {
786                 usmAgent.setSnmpContext(this);
787                 if (usmAgent.getSnmpEngineId() == null) {
788                     throw new EncodingException("UsmAgent "
789                             + usmAgent.getClass().getName()
790                             + " should provide Engine ID!");
791                 }
792                 tWindow.updateTimeWindow(usmAgent.getSnmpEngineId(),
793                         usmAgent.getSnmpEngineBoots(), usmAgent.getSnmpEngineTime(),
794                         this.isUseAuthentication());
795                 node = tWindow.getTimeLine(usmAgent.getSnmpEngineId());
796             } else {
797                 if (tWindow.isSnmpEngineIdKnown(getSendToHostAddress(), hostPort) == false) {
798                     throw new EncodingException("Engine ID of host "
799                             + getSendToHostAddress()
800                             + ", port " + hostPort
801                             + " is unknown (rId="
802                             + rId + "). Perform discovery.");
803                 }
804                 String engineId = tWindow.getSnmpEngineId(getSendToHostAddress(), hostPort);
805                 node = new TimeWindowNode(engineId, 0, 0);
806 
807                 if (isUseAuthentication()) {
808                     if (tWindow.isTimeLineKnown(engineId) == true) {
809                         node = tWindow.getTimeLine(engineId);
810                     } else {
811                         throw new EncodingException("Time Line of Engine ID of host "
812                                 + getSendToHostAddress() + ", port " + hostPort + " is unknown. "
813                                 + "Perform discovery.");
814                     }
815                 }
816             }
817         }
818         return actualEncodePacket(msg_type, rId, errstat, errind, ve, node,
819                 obj);
820     }
821 
822     /**
823      * Checks the sanity of the context and returns an error message when it
824      * is not correct.
825      */
826     protected String checkContextSanity() {
827         String ret = null;
828         if (usePrivacy == true) {
829             if (userPrivacyPassword == null) {
830                 ret = "userPrivacyPassword is null, but usePrivacy is true";
831             } else if (userPrivacyPassword.length() == 0) {
832                 ret = "userPrivacyPassword is empty, but usePrivacy is true";
833             } else if (useAuthentication == false) {
834                 ret = "useAuthentication is false, but usePrivacy is true";
835             }
836         }
837 
838         if (useAuthentication == true) {
839             if (userAuthenticationPassword == null) {
840                 ret = "userAuthenticationPassword is null, but useAuthentication is true";
841             } else if (userAuthenticationPassword.length() == 0) {
842                 ret = "userAuthenticationPassword is empty, but useAuthentication is true";
843             }
844         }
845         return ret;
846     }
847 
848     /**
849      * Does the actual encoding.
850      *
851      * @see #encodeDiscoveryPacket
852      * @see #encodePacket
853      */
854     protected byte[] actualEncodePacket(byte msg_type, int rId, int errstat,
855             int errind, Enumeration ve, TimeWindowNode node, Object obj)
856             throws IOException, EncodingException {
857         AsnEncoderv3 enc = new AsnEncoderv3();
858         String msg = checkContextSanity();
859         if (msg != null) {
860             throw new EncodingException(msg);
861         }
862 
863         int msgId = ((Integer) obj).intValue();
864         if (AsnObject.debug > 6) {
865             System.out.println(getClass().getName() + ".actualEncodePacket(): msgId="
866                     + msgId + ", Pdu reqId=" + rId);
867         }
868         byte[] packet = enc.EncodeSNMPv3(this, msgId, node,
869                 msg_type, rId, errstat, errind, ve);
870 
871         return packet;
872     }
873 
874     /**
875      * Processes an incoming SNMP v3 response.
876      */
877     protected void processIncomingResponse(ByteArrayInputStream in)
878             throws DecodingException, IOException {
879         AsnDecoderv3 rpdu = new AsnDecoderv3();
880         // don't have to check for context sanity here: if the request was
881         // fine, so should be the response
882         byte[] bu = null;
883         // need to duplicate the message for V3 to rewrite
884         int nb = in.available();
885         bu = new byte[nb];
886         in.read(bu);
887         in = new ByteArrayInputStream(bu);
888 
889         AsnSequence asnTopSeq = rpdu.DecodeSNMPv3(in);
890         int msgId = rpdu.getMessageId(asnTopSeq);
891         Integer rid = (Integer) msgIdHash.get(new Integer(msgId));
892         if (rid != null) {
893             if (AsnObject.debug > 6) {
894                 System.out.println(getClass().getName() + ".processIncomingResponse(): msgId="
895                         + msgId + ", Pdu reqId=" + rid);
896             }
897             Pdu pdu = getPdu(rid);
898             try {
899                 AsnPduSequence pduSeq = rpdu.processSNMPv3(this, asnTopSeq, bu, false);
900                 if (pduSeq != null) {
901                     // got a message
902                     Integer rid2 = new Integer(pduSeq.getReqId());
903                     if (AsnObject.debug > 6) {
904                         System.out.println(getClass().getName() + ".processIncomingResponse():"
905                                 + " rid2=" + rid2);
906                     }
907 
908                     Pdu newPdu = null;
909                     if (rid2.intValue() != rid.intValue()) {
910                         newPdu = getPdu(rid2);
911                         if (AsnObject.debug > 3) {
912                             System.out.println(getClass().getName() + ".processIncomingResponse(): "
913                                     + "pduReqId of msgId (" + rid.intValue()
914                                     + ") != pduReqId of Pdu (" + rid2.intValue()
915                                     + ")");
916                         }
917                         if (newPdu == null) {
918                             if (AsnObject.debug > 3) {
919                                 System.out.println(getClass().getName() + ".processIncomingResponse(): "
920                                         + "Using pduReqId of msgId (" + rid.intValue() + ")");
921                             }
922                         }
923                     }
924 
925                     if (newPdu != null) {
926                         pdu = newPdu;
927                     }
928                 } else {
929                     if (AsnObject.debug > 6) {
930                         System.out.println(getClass().getName() + ".processIncomingResponse():"
931                                 + " pduSeq is null.");
932                     }
933                 }
934 
935                 if (pdu != null) {
936                     pdu.fillin(pduSeq);
937                 } else {
938                     if (AsnObject.debug > 6) {
939                         System.out.println(getClass().getName() + ".processIncomingResponse(): No Pdu with reqid "
940                                 + rid.intValue());
941                     }
942                 }
943             } catch (DecodingException exc) {
944                 if (pdu != null) {
945                     pdu.setErrorStatus(AsnObject.SNMP_ERR_DECODING_EXC, exc);
946                     pdu.fillin(null);
947                 } else {
948                     throw exc;
949                 }
950             }
951         } else {
952             if (AsnObject.debug > 3) {
953                 System.out.println(getClass().getName() + ".processIncomingResponse(): Pdu of msgId " + msgId
954                         + " is already answered");
955             }
956             rid = new Integer(-1);
957         }
958     }
959 
960     /**
961      * Returns if we send this PDU in authoritative role or not.
962      * The engine who sends a Response, a Trapv2 or a Report is
963      * authoritative.
964      *
965      * @since 4_14
966      * @return true if authoritative, false if not.
967      */
968     // Note: for when adding INFORM
969     // When sending an INFORM, the receiver is the authoritative engine, so
970     // the INFORM does NOT have to be added to this list!
971     protected boolean isAuthoritative(byte msg_type) {
972         return (msg_type == AsnObject.GET_RSP_MSG
973                 ||
974                 msg_type == AsnObject.TRPV2_REQ_MSG
975                 ||
976                 msg_type == AsnObject.GET_RPRT_MSG);
977     }
978 
979     void discoverIfNeeded(Pdu pdu)
980             throws IOException, PduException {
981         UsmDiscoveryBean discBean = null;
982         boolean isNeeded = false;
983 
984         TimeWindow tWindow = TimeWindow.getCurrent();
985         String engineId = tWindow.getSnmpEngineId(getSendToHostAddress(), hostPort);
986         if (engineId == null) {
987             isNeeded = true;
988             discBean = new UsmDiscoveryBean(
989                     getSendToHostAddress(), hostPort, bindAddr, typeSocket);
990             discBean.setRetryIntervals(pdu.getRetryIntervals());
991         }
992 
993         if (isUseAuthentication()) {
994             if (isNeeded) {
995                 discBean.setAuthenticationDetails(userName,
996                         userAuthenticationPassword, authenticationProtocol);
997             } else if (tWindow.isTimeLineKnown(engineId) == false) {
998                 isNeeded = true;
999                 discBean = new UsmDiscoveryBean(
1000                         getSendToHostAddress(), hostPort, bindAddr, typeSocket);
1001                 discBean.setAuthenticationDetails(userName,
1002                         userAuthenticationPassword, authenticationProtocol);
1003                 discBean.setRetryIntervals(pdu.getRetryIntervals());
1004             }
1005 
1006             if (isNeeded && isUsePrivacy()) {
1007                 discBean.setPrivacyDetails(userPrivacyPassword, privacyProtocol);
1008             }
1009         }
1010 
1011         if (isNeeded) {
1012             discBean.startDiscovery();
1013 
1014         }
1015 
1016         // If contextEngineId is null or of length zero, set
1017         // it to the snmpEngineId.
1018         if (contextEngineId == null || contextEngineId.length == 0) {
1019             engineId = tWindow.getSnmpEngineId(getSendToHostAddress(), hostPort);
1020             setContextEngineId(SnmpUtilities.toBytes(engineId));
1021         }
1022     }
1023 
1024     /**
1025      * Adds the specified request pdu listener to receive PDUs on the
1026      * specified listening context that matches this context.
1027      * This method will call usmAgent.setSnmpContext(this).
1028      *
1029      * <p>
1030      * Don't use the TCP_SOCKET when listening for request PDUs. It doesn't
1031      * provide functionality to send a response back.
1032      * </p>
1033      *
1034      * @see AbstractSnmpContext#addRequestPduListener(RequestPduListener,
1035      *      ListeningContextPool)
1036      *
1037      * @param l        The request PDU listener
1038      * @param lcontext The listening context
1039      */
1040     public void addRequestPduListener(RequestPduListener l, ListeningContextPool lcontext)
1041             throws IOException {
1042         super.addRequestPduListener(l, lcontext);
1043 
1044         usmAgent.setSnmpContext(this);
1045         TimeWindow tWindow = TimeWindow.getCurrent();
1046         if (usmAgent.getSnmpEngineId() == null) {
1047             throw new IOException("UsmAgent "
1048                     + usmAgent.getClass().getName()
1049                     + " should provide Engine ID!");
1050         }
1051         tWindow.setSnmpEngineId(usmAgent.MYFAKEHOSTNAME, hostPort, usmAgent.getSnmpEngineId());
1052         tWindow.updateTimeWindow(usmAgent.getSnmpEngineId(),
1053                 usmAgent.getSnmpEngineBoots(), usmAgent.getSnmpEngineTime(),
1054                 this.isUseAuthentication());
1055     }
1056 
1057     /**
1058      * Copies all parameters into another SnmpContextv3.
1059      */
1060     public Object cloneParameters(SnmpContextv3Face clContext) {
1061         clContext.setUserName(new String(userName));
1062         clContext.setUseAuthentication(useAuthentication);
1063         if (userAuthenticationPassword != null) {
1064             clContext.setUserAuthenticationPassword(
1065                     new String(userAuthenticationPassword));
1066         }
1067         clContext.setAuthenticationProtocol(authenticationProtocol);
1068 
1069         clContext.setUsePrivacy(usePrivacy);
1070         if (userPrivacyPassword != null) {
1071             clContext.setUserPrivacyPassword(new String(userPrivacyPassword));
1072         }
1073         clContext.setPrivacyProtocol(privacyProtocol);
1074 
1075         clContext.setContextName(new String(contextName));
1076 
1077         int l = contextEngineId.length;
1078         byte[] newContextEngineId = new byte[l];
1079         System.arraycopy(contextEngineId, 0, newContextEngineId, 0, l);
1080         clContext.setContextEngineId(newContextEngineId);
1081 
1082         clContext.setUsmAgent(usmAgent);
1083         return clContext;
1084     }
1085 
1086     /**
1087      * Returns the hash key. This key is built out of all properties. It
1088      * serves as key for a hashtable of (v3) contexts.
1089      *
1090      * @since 4_14
1091      * @return The hash key
1092      */
1093     public String getHashKey() {
1094         StringBuffer buffer = new StringBuffer();
1095         buffer.append(hostname);
1096         buffer.append("_").append(hostPort);
1097         buffer.append("_").append(bindAddr);
1098         buffer.append("_").append(typeSocket);
1099         buffer.append("_").append(useAuthentication);
1100         buffer.append("_").append(PROTOCOL_NAMES[authenticationProtocol]);
1101         buffer.append("_").append(PROTOCOL_NAMES[privacyProtocol]);
1102         buffer.append("_").append(userAuthenticationPassword);
1103         buffer.append("_").append(userName);
1104         buffer.append("_").append(usePrivacy);
1105         buffer.append("_").append(userPrivacyPassword);
1106         buffer.append("_").append(SnmpUtilities.toHexString(contextEngineId));
1107         buffer.append("_").append(contextName);
1108         buffer.append("_v").append(getVersion());
1109 
1110         return buffer.toString();
1111     }
1112 
1113     /**
1114      * Returns a string representation of the object.
1115      * 
1116      * @return The string
1117      */
1118     public String toString() {
1119         StringBuffer buffer = new StringBuffer(getClass().getName() + "[");
1120         buffer.append("host=").append(hostname);
1121         buffer.append(", sendToHost=").append(getSendToHostAddress());
1122         buffer.append(", port=").append(hostPort);
1123         buffer.append(", bindAddress=").append(bindAddr);
1124         buffer.append(", socketType=").append(typeSocket);
1125         buffer.append(", contextEngineId=").append(SnmpUtilities.toHexString(contextEngineId));
1126         buffer.append(", contextName=").append(contextName);
1127         buffer.append(", userName=").append(userName);
1128         buffer.append(", useAuthentication=").append(useAuthentication);
1129         buffer.append(", authenticationProtocol=").append(PROTOCOL_NAMES[authenticationProtocol]);
1130         buffer.append(", userAuthenticationPassword=").append(userAuthenticationPassword);
1131         buffer.append(", usePrivacy=").append(usePrivacy);
1132         buffer.append(", privacyProtocol=").append(PROTOCOL_NAMES[privacyProtocol]);
1133         buffer.append(", userPrivacyPassword=").append(userPrivacyPassword);
1134         buffer.append(", #trapListeners=").append(trapSupport.getListenerCount());
1135         buffer.append(", #pduListeners=").append(pduSupport.getListenerCount());
1136         buffer.append("]");
1137         return buffer.toString();
1138     }
1139 
1140     /**
1141      * Generates the privacy key based on the authentication protocol.
1142      *
1143      * @param engineId               The SNMP engine ID.
1144      * @param authenticationProtocol The authentication protocol.
1145      * @param privacyProtocol        The privacyProtocol.
1146      * @return The generated privacy key.
1147      */
1148 	protected byte[] generatePrivacyKey(String engineId, int authenticationProtocol, int privacyProtocol) {
1149 		byte[] derivedPrivacyKey;
1150 		byte[] localizedPrivacyKey = null;
1151 		switch (authenticationProtocol) {
1152 			case MD5_PROTOCOL:
1153 				if (privacyProtocol != AES_ENCRYPT && privacyProtocol != DES_ENCRYPT) {
1154 					throw new IllegalArgumentException(
1155 							"Unsupported privacy protocol for MD5: " + PROTOCOL_NAMES[privacyProtocol]);
1156 				}
1157 				derivedPrivacyKey = getPrivacyPasswordKeyMD5();
1158 				return SnmpUtilities.getLocalizedKeyMD5(derivedPrivacyKey, engineId);
1159 			case SHA1_PROTOCOL:
1160 				if (privacyProtocol != AES_ENCRYPT && privacyProtocol != DES_ENCRYPT) {
1161 					throw new IllegalArgumentException(
1162 							"Unsupported privacy protocol for SHA1: " + PROTOCOL_NAMES[privacyProtocol]);
1163 				}
1164 				derivedPrivacyKey = getPrivacyPasswordKeySHA1();
1165 				return SnmpUtilities.getLocalizedKeySHA1(derivedPrivacyKey, engineId);
1166 			case SHA224_PROTOCOL: {
1167 				if (privacyProtocol != AES_ENCRYPT && privacyProtocol != DES_ENCRYPT
1168 						&& privacyProtocol != AES192_ENCRYPT) {
1169 					throw new IllegalArgumentException(
1170 							"Unsupported privacy protocol for SHA224: " + PROTOCOL_NAMES[privacyProtocol]);
1171 				}
1172 				derivedPrivacyKey = getPrivacyPasswordKeySHA224();
1173 				return SnmpUtilities.getLocalizedKeySHA224(derivedPrivacyKey, engineId);
1174 			}
1175 			case SHA256_PROTOCOL: {
1176 				derivedPrivacyKey = getPrivacyPasswordKeySHA256();
1177 				localizedPrivacyKey = deriveKey(engineId, derivedPrivacyKey, SnmpUtilities::getLocalizedKeySHA256,
1178 						privacyProtocol);
1179 				return localizedPrivacyKey;
1180 			}
1181 			case SHA384_PROTOCOL: {
1182 				derivedPrivacyKey = getPrivacyPasswordKeySHA384();
1183 				localizedPrivacyKey = deriveKey(engineId, derivedPrivacyKey, SnmpUtilities::getLocalizedKeySHA384,
1184 						privacyProtocol);
1185 				return localizedPrivacyKey;
1186 			}
1187 			case SHA512_PROTOCOL: {
1188 				derivedPrivacyKey = getPrivacyPasswordKeySHA512();
1189 				localizedPrivacyKey = deriveKey(engineId, derivedPrivacyKey, SnmpUtilities::getLocalizedKeySHA512,
1190 						privacyProtocol);
1191 				return localizedPrivacyKey;
1192 			}
1193 			default:
1194 				throw new IllegalArgumentException("Unsupported authentication protocol: " + authenticationProtocol);
1195 			}
1196 	}
1197     
1198     /**
1199      * Derives a final privacy key
1200      *
1201      * @param engineId          The SNMP engine ID.
1202      * @param derivedPrivacyKey The SNMP engine ID.
1203      * @param localizeKey         A function that localizes the key using the derived key and engine ID.
1204      * @param privacyProtocol   The privacyProtocol
1205      * @return The derived privacy key.
1206      */
1207     private static byte[] deriveKey(
1208     		String engineId,
1209     		byte[] derivedPrivacyKey,
1210     		BiFunction<byte[],
1211     		String, byte[]> localizeKey,
1212     		int privacyProtocol) {
1213         byte[] localizedPrivacyKeyBase = localizeKey.apply(derivedPrivacyKey, engineId);
1214         switch (privacyProtocol) {
1215             case AES_ENCRYPT:
1216             case AES192_ENCRYPT:
1217             case AES256_ENCRYPT:
1218             case DES_ENCRYPT:
1219 				return localizedPrivacyKeyBase;
1220             default:
1221                 throw new IllegalArgumentException("Unsupported privacy protocol: " + PROTOCOL_NAMES[privacyProtocol]);
1222         }
1223      }
1224  
1225 
1226     /**
1227      * Computes the fingerprint for the given SNMP message.
1228      *
1229      * @param snmpEngineId           The SNMP engine ID.
1230      * @param authenticationProtocol The authentication protocol.
1231      * @param computedFingerprint    The computed fingerprint.
1232      * @param message                The SNMP message.
1233      * @return The computed fingerprint.
1234      */
1235     protected byte[] computeFingerprint(String snmpEngineId, int authenticationProtocol, byte[] computedFingerprint,
1236             byte[] message) {
1237         if (authenticationProtocol == MD5_PROTOCOL) {
1238             byte[] passwKey = getAuthenticationPasswordKeyMD5();
1239             byte[] authkey = SnmpUtilities.getLocalizedKeyMD5(passwKey, snmpEngineId);
1240             computedFingerprint = SnmpUtilities.getFingerPrintMD5(authkey, message);
1241         } else if (authenticationProtocol == SHA1_PROTOCOL) {
1242             byte[] passwKey = getAuthenticationPasswordKeySHA1();
1243             byte[] authkey = SnmpUtilities.getLocalizedKeySHA1(passwKey, snmpEngineId);
1244             computedFingerprint = SnmpUtilities.getFingerPrintSHA1(authkey, message);
1245         } else if (authenticationProtocol == SHA256_PROTOCOL) {
1246             byte[] passwKey = getAuthenticationPasswordKeySHA256();
1247             byte[] authkey = SnmpUtilities.getLocalizedKeySHA256(passwKey, snmpEngineId);
1248             computedFingerprint = SnmpUtilities.getFingerPrintSHA256(authkey, message);
1249         } else if (authenticationProtocol == SHA512_PROTOCOL) {
1250             byte[] passwKey = getAuthenticationPasswordKeySHA512();
1251             byte[] authkey = SnmpUtilities.getLocalizedKeySHA512(passwKey, snmpEngineId);
1252             computedFingerprint = SnmpUtilities.getFingerPrintSHA512(authkey, message);
1253         } else if (authenticationProtocol == SHA224_PROTOCOL) {
1254             byte[] passwKey = getAuthenticationPasswordKeySHA224();
1255             byte[] authkey = SnmpUtilities.getLocalizedKeySHA224(passwKey, snmpEngineId);
1256             computedFingerprint = SnmpUtilities.getFingerPrintSHA224(authkey, message);
1257         } else if (authenticationProtocol == SHA384_PROTOCOL) {
1258             byte[] passwKey = getAuthenticationPasswordKeySHA384();
1259             byte[] authkey = SnmpUtilities.getLocalizedKeySHA384(passwKey, snmpEngineId);
1260             computedFingerprint = SnmpUtilities.getFingerPrintSHA384(authkey, message);
1261         }
1262         return computedFingerprint;
1263     }
1264 
1265 }