View Javadoc
1   // NAME
2   //      $RCSfile: Pdu.java,v $
3   // DESCRIPTION
4   //      [given below in javadoc format]
5   // DELTA
6   //      $Revision: 3.33 $
7   // CREATED
8   //      $Date: 2008/12/12 14:55:51 $
9   // COPYRIGHT
10  //      Westhawk Ltd
11  // TO DO
12  //
13  
14  /*
15   * Copyright (C) 1995, 1996 by West Consulting BV
16   *
17   * Permission to use, copy, modify, and distribute this software
18   * for any purpose and without fee is hereby granted, provided
19   * that the above copyright notices appear in all copies and that
20   * both the copyright notice and this permission notice appear in
21   * supporting documentation.
22   * This software is provided "as is" without express or implied
23   * warranty.
24   * author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
25   */
26  
27  /*
28   * Copyright (C) 1996 - 2006 by Westhawk Ltd
29   * <a href="www.westhawk.co.uk">www.westhawk.co.uk</a>
30   *
31   * Permission to use, copy, modify, and distribute this software
32   * for any purpose and without fee is hereby granted, provided
33   * that the above copyright notices appear in all copies and that
34   * both the copyright notice and this permission notice appear in
35   * supporting documentation.
36   * This software is provided "as is" without express or implied
37   * warranty.
38   * author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
39   */
40  
41  
42  package uk.co.westhawk.snmp.stack;
43  
44  /*-
45   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
46   * SNMP Java Client
47   * ჻჻჻჻჻჻
48   * Copyright 2023 MetricsHub, Westhawk
49   * ჻჻჻჻჻჻
50   * This program is free software: you can redistribute it and/or modify
51   * it under the terms of the GNU Lesser General Public License as
52   * published by the Free Software Foundation, either version 3 of the
53   * License, or (at your option) any later version.
54   *
55   * This program is distributed in the hope that it will be useful,
56   * but WITHOUT ANY WARRANTY; without even the implied warranty of
57   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
58   * GNU General Lesser Public License for more details.
59   *
60   * You should have received a copy of the GNU General Lesser Public
61   * License along with this program.  If not, see
62   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
63   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
64   */
65  import java.util.*;
66  import java.io.*;
67  
68  import uk.co.westhawk.snmp.util.*;
69  
70  /**
71   * This class represents the ASN PDU object, this is the equivalent of
72   * a GetRequest PDU.
73   *
74   * @author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
75   * @version $Revision: 3.33 $ $Date: 2008/12/12 14:55:51 $
76   */
77  public abstract class Pdu extends Observable {
78      private static final String version_id = "@(#)$Id: Pdu.java,v 3.33 2008/12/12 14:55:51 tpanton Exp $ Copyright Westhawk Ltd";
79  
80      protected Vector reqVarbinds = null;
81      protected Vector respVarbinds = null;
82  
83      private final static String TIMED_OUT = "Timed out";
84      private final static String[] errorStrings = {
85              "No error",
86              "Value too big error",
87              "No such name error",
88              "Bad value error",
89              "Read only error",
90              "General error",
91              "No access error",
92              "Wrong type error",
93              "Wrong length error",
94              "Wrong encoding error",
95              "Wrong value error",
96              "No creation error",
97              "Inconsistent value error",
98              "Resource unavailable error",
99              "Commit failed error",
100             "Undo failed error",
101             "Authorization error",
102             "Not writable error",
103             "Inconsistent name error",
104     };
105 
106     private static int next_id = 1;
107     private static final Object NEXT_ID_LOCK = new Object();
108 
109     private int retry_intervals[] = { 500, 1000, 2000, 5000, 5000 };
110 
111     protected byte[] encodedPacket = null;
112     protected SnmpContextBasisFace context;
113     protected boolean added = false;
114     protected byte msg_type;
115 
116     int req_id;
117     protected Integer snmpv3MsgId = null;
118     protected int errstat;
119     protected int errind;
120 
121     private Transmitter trans = null;
122     private int retries;
123     protected boolean answered;
124     private boolean got = false;
125     private boolean isTimedOut;
126     private PduException respException = null;
127 
128     /**
129      * The value of the response is set. This will be called by
130      * Pdu.fillin().
131      */
132     protected void new_value(int n, varbind res) {
133     }
134 
135     /**
136      * This method notifies all observers.
137      * This will be called by Pdu.fillin().
138      *
139      * <p>
140      * The Object to the update() method of the Observer will be a varbind,
141      * unless an exception occurred.
142      * In the case of an exception, that exception will be passed.
143      * So watch out casting!
144      * </p>
145      */
146     protected void tell_them() {
147         notifyObservers();
148     }
149 
150     /**
151      * Constructor.
152      *
153      * @param con The context of the PDU
154      * @see SnmpContext
155      * @see SnmpContextv2c
156      * @see SnmpContextv3
157      */
158     public Pdu(SnmpContextBasisFace con) {
159         context = con;
160 
161         // TODO: would not work if we ever were to send response or report!
162         // TODO: We would have to set the req_id!
163 
164         // Added NEXT_ID_LOCK on suggestion of
165         // Victor Kirk <Victor.Kirk@serco.com>
166         synchronized (NEXT_ID_LOCK) {
167             req_id = next_id++;
168         }
169         errstat = AsnObject.SNMP_ERR_NOERROR;
170         errind = 0x00;
171         reqVarbinds = new Vector(1, 1);
172         setMsgType(AsnObject.GET_REQ_MSG);
173         isTimedOut = false;
174 
175         // if it is not set to false, PDUs that do not receive a response
176         // will not be filled in properly.
177         answered = false;
178     }
179 
180     /**
181      * Returns the context of this PDU.
182      * 
183      * @return The context
184      */
185     public SnmpContextBasisFace getContext() {
186         return context;
187     }
188 
189     /**
190      * Sets the retry intervals of the PDU. The length of the array
191      * corresponds with the number of retries. Each entry in the array
192      * is the number of milliseconds of each try.
193      *
194      * <p>
195      * If used, please set before sending!
196      * </p>
197      *
198      * The default is {500, 1000, 2000, 5000, 5000}.
199      * It is good practice to make the interval bigger with each retry,
200      * if the numbers are the same the chance of collision is higher.
201      *
202      * @param rinterval The interval in msec of each retry
203      */
204     public void setRetryIntervals(int rinterval[]) {
205         retry_intervals = rinterval;
206     }
207 
208     public int[] getRetryIntervals() {
209         return retry_intervals;
210     }
211 
212     /**
213      * Sends the PDU.
214      * Note that all properties of the context have to be set before this
215      * point.
216      */
217     public boolean send() throws IOException, PduException {
218         return send(errstat, errind);
219     }
220 
221     /**
222      * Sends the PDU. This method accommodates the GetBulk request.
223      *
224      * @param error_status The value of the error_status field.
225      * @param error_index  The value of the error_index field.
226      * @see #send()
227      */
228     protected boolean send(int error_status, int error_index)
229             throws IOException, PduException {
230         if (added == false) {
231             // Moved this statement from the constructor because it
232             // conflicts with the way the SnmpContextXPool works.
233             added = context.addPdu(this);
234         }
235         Enumeration vbs = reqVarbinds.elements();
236         encodedPacket = context.encodePacket(msg_type, req_id, error_status,
237                 error_index, vbs, snmpv3MsgId);
238         addToTrans();
239         return added;
240     }
241 
242     /**
243      * Adds the PDU to its transmitter. The transmitter is the thread that
244      * will be sent the PDU and then waits for the answer.
245      *
246      * @see #send
247      */
248     protected void addToTrans() {
249         if (added && (trans != null)) {
250             // thanks to Ian Dowse <iedowse@iedowse.com> for synchronisation
251             synchronized (trans) {
252                 trans.setPdu(this);
253                 trans.stand();
254             }
255         }
256     }
257 
258     /**
259      * Sends the actual packet and updates the retries.
260      *
261      * @see AbstractSnmpContext#sendPacket(byte[] p)
262      */
263     protected boolean sendme() {
264         context.sendPacket(encodedPacket);
265         retries++;
266 
267         if (AsnObject.debug > 6) {
268             System.out.println(getClass().getName() + ".sendme(): Sent Pdu reqId=" + req_id + ", retries " + retries);
269         }
270         return (retries > 3);
271     }
272 
273     /**
274      * Sends the PDU.
275      * For backwards compatibility only. Please use send() instead.
276      * The community name will be passed to the SnmpContext. If using
277      * SnmpContextv3, the community name will be ignored.
278      *
279      * @param com The community name of the PDU in SNMPv1 and SNMPv2c.
280      * @deprecated Community name has moved to SnmpContext. Use
281      *             {@link SnmpContext#setCommunity(String)}.
282      *
283      * @see SnmpContext#setCommunity
284      * @see #send
285      */
286     public boolean send(String com) throws IOException, PduException {
287         if (context instanceof SnmpContext) {
288             ((SnmpContext) context).setCommunity(com);
289         }
290         return send();
291     }
292 
293     /**
294      * Adds an OID (object identifier) to the PDU. The OID indicates which
295      * MIB variable we request for or which MIB variable should be set.
296      *
297      * @param oid The oid
298      * @see #addOid(varbind)
299      * @see varbind
300      */
301     public void addOid(String oid) {
302         varbind vb = new varbind(oid);
303         addOid(vb);
304     }
305 
306     /**
307      * Adds an OID (object identifier) to the PDU. The OID indicates which
308      * MIB variable we request for or which MIB variable should be set.
309      *
310      * @param oid The oid
311      * @see #addOid(varbind)
312      * @see varbind
313      * @since 4_12
314      */
315     public void addOid(AsnObjectId oid) {
316         varbind vb = new varbind(oid);
317         addOid(vb);
318     }
319 
320     /**
321      * Adds an OID (object identifier) to the PDU and the value that has
322      * to be set. This method has moved from SetPdu to this class in version 4_12.
323      *
324      * @param oid The oid
325      * @param val The value
326      * @see Pdu#addOid
327      * @see varbind
328      * @since 4_12
329      */
330     public void addOid(String oid, AsnObject val) {
331         varbind vb = new varbind(oid, val);
332         addOid(vb);
333     }
334 
335     /**
336      * Adds an OID (object identifier) to the PDU and the value that has
337      * to be set.
338      *
339      * <p>
340      * Thanks to Eli Bishop (eli@graphesthesia.com) for the suggestion.
341      * </p>
342      *
343      * @param oid The oid
344      * @param val The value
345      * @see Pdu#addOid
346      * @see varbind
347      * @since 4_12
348      */
349     public void addOid(AsnObjectId oid, AsnObject val) {
350         varbind vb = new varbind(oid, val);
351         addOid(vb);
352     }
353 
354     /**
355      * Adds an OID (object identifier) to the PDU.
356      *
357      * @param var The varbind
358      * @see #addOid(String)
359      */
360     public void addOid(varbind var) {
361         reqVarbinds.addElement(var);
362     }
363 
364     /**
365      * Returns a copy of the varbinds used to build the request.
366      *
367      * @return the request varbinds of this PDU.
368      */
369     public varbind[] getRequestVarbinds() {
370         int sz = reqVarbinds.size();
371         varbind[] arr = new varbind[sz];
372         reqVarbinds.copyInto(arr);
373         return arr;
374     }
375 
376     /**
377      * Returns a copy of the varbinds received in the response.
378      * If there was no response (yet), null will be returned.
379      *
380      * @return the response varbinds of this PDU.
381      * @exception PduException An agent or decoding exception occurred
382      *                         whilst receiving the response.
383      *
384      * @see #getErrorStatus
385      * @see #notifyObservers
386      */
387     public varbind[] getResponseVarbinds() throws PduException {
388         if (respException != null) {
389             throw respException;
390         }
391 
392         varbind[] arr = null;
393         if (respVarbinds != null) {
394             int sz = respVarbinds.size();
395             arr = new varbind[sz];
396             respVarbinds.copyInto(arr);
397         }
398         return arr;
399     }
400 
401     private void dump(Vector v, varbind[] array) {
402         int sz = v.size();
403         System.out.println("Vector: ");
404         for (int i = 0; i < sz; i++) {
405             System.out.println("\t" + v.elementAt(i));
406         }
407         System.out.println("Array: ");
408         for (int i = 0; i < array.length; i++) {
409             System.out.println("\t" + array[i]);
410         }
411         System.out.println("--");
412     }
413 
414     void setResponseException(PduException exc) {
415         if (AsnObject.debug > 6) {
416             System.out.println(getClass().getName() + ".setResponseException(): reqId=" + req_id + exc.getMessage());
417         }
418         respException = exc;
419     }
420 
421     /**
422      * Returns the request id of the PDU.
423      *
424      * @return The ID
425      */
426     public int getReqId() {
427         return req_id;
428     }
429 
430     /**
431      * Returns the error index.
432      * The error index indicates which of the OIDs went wrong.
433      *
434      * @return the error index
435      * @see #getErrorStatus
436      */
437     public int getErrorIndex() {
438         return errind;
439     }
440 
441     /**
442      * Returns the error status as indicated by the error-status field in
443      * the reponse PDU. The error index will indicated which OID caused the
444      * error.
445      * In case of a decoding exception the error status
446      * will be set to one of the decoding errors:
447      * <ul>
448      * <li>
449      * <code>SnmpConstants.SNMP_ERR_DECODING_EXC</code>.
450      * </li>
451      * <li>
452      * <code>SnmpConstants.SNMP_ERR_DECODINGASN_EXC</code>.
453      * </li>
454      * <li>
455      * <code>SnmpConstants.SNMP_ERR_DECODINGPKTLNGTH_EXC</code>.
456      * </li>
457      * </ul>
458      *
459      * <p>
460      * The actual exception will be passed to your
461      * <code>update(Observable ob, Object arg)</code>
462      * method via the the parameter
463      * <code>arg</code>.
464      * </p>
465      *
466      * @return the error status
467      * @see #notifyObservers
468      * @see #getResponseVarbinds
469      * @see SnmpConstants#SNMP_ERR_NOERROR
470      * @see SnmpConstants#SNMP_ERR_DECODING_EXC
471      * @see SnmpConstants#SNMP_ERR_DECODINGASN_EXC
472      * @see SnmpConstants#SNMP_ERR_DECODINGPKTLNGTH_EXC
473      * @see #getErrorStatusString
474      * @see #getErrorIndex
475      */
476     public int getErrorStatus() {
477         return errstat;
478     }
479 
480     /**
481      * Returns the string representation of the error status.
482      *
483      * @return the error string
484      * @see #getErrorStatus
485      */
486     public String getErrorStatusString() {
487         String errString = "";
488         if (errstat >= 0) {
489             if (errstat < errorStrings.length) {
490                 errString = errorStrings[errstat];
491 
492                 if (errstat == AsnObject.SNMP_ERR_GENERR
493                         &&
494                         isTimedOut() == true) {
495                     errString = TIMED_OUT;
496                 }
497             } else {
498                 // they much be one of the DECODING*_EXC
499                 if (respException != null) {
500                     errString = respException.getMessage();
501                 } else {
502                     errString = "Decoding Exception";
503                 }
504             }
505         }
506         return errString;
507     }
508 
509     /**
510      * Returns whether or not this PDU is timed out, i.e. it did not get
511      * a response.
512      * Its errorStatus will be set to AsnObject.SNMP_ERR_GENERR.
513      *
514      * <p>
515      * Note that a SNMP agent can respond with an errorStatus of
516      * AsnObject.SNMP_ERR_GENERR as well, so getting a
517      * AsnObject.SNMP_ERR_GENERR does not necessarily mean that the request
518      * is timed out!
519      * </p>
520      *
521      * @return true is the PDU was timed out
522      * @see #getErrorStatus
523      * @see SnmpConstants#SNMP_ERR_GENERR
524      */
525     public boolean isTimedOut() {
526         return isTimedOut;
527     }
528 
529     /**
530      * This method will wait until the answer is received, instead of
531      * continue with other stuff.
532      */
533     public boolean waitForSelf() {
534         // Add an extra second to the waiting. This gives the PDU a chance
535         // to handle the timeout correctly before this thread wakes up.
536         long del = 1000;
537         for (int i = 0; i < retry_intervals.length; i++) {
538             del += retry_intervals[i];
539         }
540         boolean res = waitForSelf(del);
541 
542         if (AsnObject.debug > 6) {
543             System.out.println(getClass().getName() + ".waitForSelf(): reqId=" + req_id + " " + res);
544         }
545 
546         // Should I??
547         if (!answered) {
548             handleNoAnswer();
549         }
550 
551         return res;
552     }
553 
554     /**
555      * Returns the string representation of the PDU.
556      *
557      * @return The string of the PDU
558      */
559     public String toString() {
560         return toString(false);
561     }
562 
563     /**
564      * Returns the string representation of the PDU with or without the
565      * response varbinds.
566      *
567      * @param withRespVars Include the response varbinds or not
568      * @return The string of the PDU
569      */
570     protected String toString(boolean withRespVars) {
571         StringBuffer buffer = new StringBuffer(getClass().getName());
572         buffer.append("[");
573         buffer.append("context=").append(context);
574         buffer.append(", reqId=").append(req_id);
575         buffer.append(", msgType=0x").append(SnmpUtilities.toHex(msg_type));
576 
577         buffer.append(", ");
578         buffer.append(printVars("reqVarbinds", reqVarbinds));
579 
580         if (withRespVars == true) {
581             buffer.append(", ");
582             buffer.append(printVars("respVarbinds", respVarbinds));
583         }
584 
585         buffer.append("]");
586         return buffer.toString();
587     }
588 
589     /**
590      * Returns the string representation of the varbinds of the PDU.
591      *
592      * @see #toString(boolean)
593      * @since 4_14
594      */
595     protected StringBuffer printVars(String title, Vector vars) {
596         StringBuffer buffer = new StringBuffer();
597         buffer.append(title).append("=");
598         if (vars != null) {
599             int sz = vars.size();
600             buffer.append("[");
601             for (int i = 0; i < sz; i++) {
602                 if (i > 0) {
603                     buffer.append(", ");
604                 }
605                 varbind var = (varbind) vars.elementAt(i);
606                 buffer.append(var.toString());
607             }
608             buffer.append("]");
609         } else {
610             buffer.append("null");
611         }
612         return buffer;
613     }
614 
615     synchronized boolean waitForSelf(long delay) {
616         if (!got) {
617             try {
618                 wait(delay);
619             } catch (InterruptedException ix) {
620                 ;
621             }
622         }
623         return answered;
624     }
625 
626     void transmit() {
627         transmit(true);
628     }
629 
630     void transmit(boolean withRetries) {
631         if (withRetries == true) {
632             int n = 0;
633             answered = false;
634 
635             while ((!context.isDestroyed()) && (!answered) && (n < retry_intervals.length)) {
636                 sendme();
637 
638                 try {
639                     Thread.sleep(retry_intervals[n]);
640                 } catch (InterruptedException e) {
641                 }
642                 n++;
643             }
644 
645             if (!answered) {
646                 handleNoAnswer();
647             }
648         } else {
649             // just send it once. this will only happen if we are in a trap
650             // or response PDU.
651             sendme();
652             answered = true;
653         }
654 
655         if (!context.removePdu(req_id)) {
656             if (AsnObject.debug > 6) {
657                 System.out.println(getClass().getName() + ".transmit(): Failed to remove reqId " + req_id);
658             }
659         }
660     }
661 
662     void setTrans(Transmitter t) {
663         trans = t;
664     }
665 
666     /**
667      * Returns the message type, this will indicate what kind of request we
668      * are dealing with.
669      * By default it will be set to the GET_REQ_MSG
670      *
671      * @return The message type
672      */
673     public byte getMsgType() {
674         return msg_type;
675     }
676 
677     /**
678      * Sets the message type, this will indicate what kind of request we
679      * are dealing with.
680      * By default it will be set to the GET_REQ_MSG
681      *
682      * @param type The message type
683      */
684     protected void setMsgType(byte type) {
685         msg_type = type;
686     }
687 
688     /**
689      * Sets the error status, indicating what went wrong.
690      *
691      * @param err the error status
692      * @see #getErrorIndex
693      * @see #getErrorStatusString
694      * @see #getErrorStatus
695      */
696     protected void setErrorStatus(int err) {
697         errstat = err;
698         if (AsnObject.debug > 6) {
699             System.out.println(getClass().getName() + ".setErrorStatus(): reqId=" + req_id + " " + errstat);
700         }
701         if (errstat != AsnObject.SNMP_ERR_NOERROR) {
702             setResponseException(new AgentException(getErrorStatusString()));
703         }
704     }
705 
706     /**
707      * Sets the error status and the exception, indicating what went wrong.
708      *
709      * @param err the error status
710      * @param exc the PDU Exception that was thrown whilst decoding
711      *
712      * @see #getErrorIndex
713      * @see #getErrorStatusString
714      * @see #getErrorStatus
715      */
716     protected void setErrorStatus(int err, PduException exc) {
717         errstat = err;
718         setResponseException(exc);
719     }
720 
721     /**
722      * Sets the error index, this indicates which of the OIDs went wrong.
723      * 
724      * @param ind the error index
725      * @see #setErrorStatus(int)
726      * @see #getErrorIndex
727      */
728     protected void setErrorIndex(int ind) {
729         errind = ind;
730     }
731 
732     /**
733      * Returns whether or not this type of PDU is expecting some kind of response.
734      * This method is used in AbstractSnmpContext to help determine whether
735      * or not to start a thread that listens for a response when sending this
736      * PDU.
737      * The default is <em>true</em>.
738      *
739      * @return true if a response is expected, false if not.
740      * @since 4_14
741      */
742     protected boolean isExpectingResponse() {
743         return true;
744     }
745 
746     /**
747      * This method is called when no answer is received after all
748      * retries.
749      * The application is notified of this.
750      * See also fillin()
751      */
752     private void handleNoAnswer() {
753         if (AsnObject.debug > 6) {
754             System.out.println(getClass().getName() + ".handleNoAnswer(): reqId=" + req_id);
755         }
756 
757         // it's a lie, but it will prevent this method from
758         // being called twice
759         answered = true;
760 
761         isTimedOut = true;
762         setErrorStatus(AsnObject.SNMP_ERR_GENERR);
763         setErrorIndex(0);
764 
765         setChanged();
766         tell_them();
767         clearChanged();
768 
769         synchronized (this) {
770             notify();
771         }
772     }
773 
774     /**
775      * Fill in the received response.
776      * 
777      * @see Pdu#getResponseVarbinds()
778      *
779      */
780     void fillin(AsnPduSequence seq) {
781         if (answered) {
782             if (AsnObject.debug > 6) {
783                 System.out.println(getClass().getName() + ".fillin(): Got a second answer to reqId " + req_id);
784             }
785             return;
786         }
787 
788         // fillin(null) can be called in case of a Decoding exception
789         if (seq != null) {
790             if (seq.isCorrect == true) {
791                 int n = -1;
792                 try {
793                     // Fill in the request id
794                     this.req_id = seq.getReqId();
795                     setErrorStatus(seq.getWhatError());
796                     setErrorIndex(seq.getWhereError());
797 
798                     // The varbinds from the response/report are set in a
799                     // new Vector.
800                     AsnSequence varBind = seq.getVarBind();
801                     int size = varBind.getObjCount();
802                     respVarbinds = new Vector(size, 1);
803                     for (n = 0; n < size; n++) {
804                         Object obj = varBind.getObj(n);
805                         if (obj instanceof AsnSequence) {
806                             AsnSequence varSeq = (AsnSequence) obj;
807                             try {
808                                 varbind vb = new varbind(varSeq);
809                                 respVarbinds.addElement(vb);
810                                 new_value(n, vb);
811                             } catch (IllegalArgumentException exc) {
812                             }
813                         }
814                     }
815 
816                     // At this point, I don't know whether I received a
817                     // response and should fill in only the respVarbind or
818                     // whether I received a request (via ListeningContext)
819                     // and I should fill in the reqVarbinds.
820                     // So when reqVarbinds is empty, I clone the
821                     // respVarbinds.
822                     if (reqVarbinds.isEmpty()) {
823                         reqVarbinds = (Vector) respVarbinds.clone();
824                     }
825                 } catch (Exception e) {
826                     // it happens that an agent does not encode the varbind
827                     // list properly. Since we try do decode as much as
828                     // possible there may be wrong elements in this list.
829 
830                     DecodingException exc = new DecodingException(
831                             "Incorrect varbind list, element " + n);
832                     setErrorStatus(AsnObject.SNMP_ERR_DECODINGASN_EXC, exc);
833                 }
834             } else {
835                 // we couldn't read the whole message
836                 // see AsnObject.AsnReadHeader, isCorrect
837 
838                 DecodingException exc = new DecodingException(
839                         "Incorrect packet. No of bytes received less than packet length.");
840                 setErrorStatus(AsnObject.SNMP_ERR_DECODINGPKTLNGTH_EXC, exc);
841             }
842         }
843 
844         // always do 'setChanged', even if there are no varbinds.
845         setChanged();
846         tell_them();
847         clearChanged();
848 
849         synchronized (this) {
850             got = true;
851             answered = true;
852             notify(); // see also handleNoAnswer()
853             if (trans != null) {
854                 // free up the transmitter, since
855                 // we are happy with the answer.
856                 // trans may be null if we are receiving a trap.
857                 trans.interruptMe();
858             }
859         }
860     }
861 
862     /**
863      * Notify all observers. If a decoding exception had occurred, the argument
864      * will be replaced with the exception.
865      *
866      * <p>
867      * In the case of an exception, the error status
868      * will be set to one of the decoding errors (see
869      * <code>getErrorStatus</code>)
870      * and passed as the parameter
871      * <code>arg</code> in the
872      * <code>update(Observable obs, Object arg)</code>
873      * method.
874      * </p>
875      *
876      * @param arg The argument passed to update, can be a PduException.
877      * @see SnmpConstants#SNMP_ERR_DECODING_EXC
878      * @see #getErrorStatus
879      * @see #getResponseVarbinds
880      * @since 4.5
881      */
882     public void notifyObservers(Object arg) {
883         if (respException != null) {
884             super.notifyObservers(respException);
885         } else {
886             super.notifyObservers(arg);
887         }
888     }
889 
890 }