1 // NAME 2 // $RCSfile: MultiResponsePdu.java,v $ 3 // DESCRIPTION 4 // [given below in javadoc format] 5 // DELTA 6 // $Revision: 3.3 $ 7 // CREATED 8 // $Date: 2007/10/17 10:44:09 $ 9 // COPYRIGHT 10 // Westhawk Ltd 11 // TO DO 12 // 13 14 /* 15 * Copyright (C) 2006 by Westhawk Ltd 16 * <a href="www.westhawk.co.uk">www.westhawk.co.uk</a> 17 * 18 * Permission to use, copy, modify, and distribute this software 19 * for any purpose and without fee is hereby granted, provided 20 * that the above copyright notices appear in all copies and that 21 * both the copyright notice and this permission notice appear in 22 * supporting documentation. 23 * This software is provided "as is" without express or implied 24 * warranty. 25 * author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a> 26 */ 27 28 package uk.co.westhawk.snmp.stack; 29 30 /*- 31 * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ 32 * SNMP Java Client 33 * ჻჻჻჻჻჻ 34 * Copyright 2023 MetricsHub, Westhawk 35 * ჻჻჻჻჻჻ 36 * This program is free software: you can redistribute it and/or modify 37 * it under the terms of the GNU Lesser General Public License as 38 * published by the Free Software Foundation, either version 3 of the 39 * License, or (at your option) any later version. 40 * 41 * This program is distributed in the hope that it will be useful, 42 * but WITHOUT ANY WARRANTY; without even the implied warranty of 43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 44 * GNU General Lesser Public License for more details. 45 * 46 * You should have received a copy of the GNU General Lesser Public 47 * License along with this program. If not, see 48 * <http://www.gnu.org/licenses/lgpl-3.0.html>. 49 * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ 50 */ 51 52 import java.util.*; 53 54 55 /** 56 * This class can receive multiple responses. 57 * Typical usage includes sending a single PDU to a multicast / 58 * broadcast address so that multiple responses can be received from 59 * different sources. 60 * 61 * <p> 62 * This class sets a single long timeout for the retry, so it sends the 63 * request only once. 64 * Opposite to its parent class, this class does not ignore the duplicate 65 * responses, and it will timeout by nature. 66 * </p> 67 * 68 * <p>Note:</p> 69 * <ul> 70 * <li>Please realise that you might choke the stack and your network, when 71 * you use this class, even on a small subnet</li> 72 * <li>This PDU will eat up transmit and receive resources, until it times out</li> 73 * <li>This PDU cannot be used to receive traps</li> 74 * <li>Authentication (and privacy) is by definition a unicast activity. 75 * You can find unauthenticated SNMPv3 engines, by broadcasting this PDU 76 * with a SnmpContextv3(Pool) with no authentication. 77 * Then you need to continue an authentication/privacy context and a (normal) 78 * PDU.<br/> 79 * In other words, finding SNMPv3 engines that only support 80 * authentication and/or privacy cannot be done via broadcasting. 81 * </li> 82 * </ul> 83 * 84 * 85 * <p> 86 * Thanks to Josh Bers <jbers@bbn.com> 87 * </p> 88 * 89 * @since 4_14 90 * @author <a href="mailto:snmp@westhawk.co.uk">Birgit Arkesteijn</a> 91 * @version $Revision: 3.3 $ $Date: 2007/10/17 10:44:09 $ 92 */ 93 public class MultiResponsePdu extends Pdu { 94 private static final String version_id = "@(#)$Id: MultiResponsePdu.java,v 3.3 2007/10/17 10:44:09 birgita Exp $ Copyright Westhawk Ltd"; 95 96 /** 97 * Hashtable to hold responses received from agents. 98 */ 99 private Hashtable responses = new Hashtable(); 100 101 /** 102 * IP address of current response 103 */ 104 private String thisIP = null; 105 106 /** 107 * By default create a MultiResponsePdu that will wait for 3 108 * seconds for responses to come in from multiple sources. If you 109 * want to wait longer set the RetryInterval to a longer first 110 * timeout. To make the request more reliable, add more timeouts. 111 * 112 * @param con The context 113 */ 114 public MultiResponsePdu(SnmpContextBasisFace con) { 115 super(con); 116 setRetryIntervals(new int[] { 3000 }); 117 } 118 119 /** 120 * Gets the IP address of the host of the most recent response received. 121 * 122 * @return The sourceAgent value 123 */ 124 public String getSourceAgent() { 125 return thisIP; 126 } 127 128 /** 129 * Gets the number of responses so far received 130 * to this request. 131 * 132 * @return The number of responses 133 */ 134 public int getNumResponses() { 135 return responses.size(); 136 } 137 138 /** 139 * Prints out the list of received responses and their source IP 140 * addressses. Results will be ommitted if not yet received. 141 * 142 * @return String representation of this PDU and all its received 143 * responses 144 */ 145 public String toString() { 146 // loop over vector of responses and use Pdu.toString(boolean) 147 // to print out the received varbinds. 148 StringBuffer buffer = new StringBuffer(); 149 if (!answered) { 150 buffer.append(toString(false)); 151 } else { 152 Enumeration ipaddrs = responses.keys(); 153 String ipaddr = (String) ipaddrs.nextElement(); 154 // set respVarbinds to each response in turn calling toString(true) 155 respVarbinds = (Vector) responses.get(ipaddr); 156 157 buffer.append(toString(true)); 158 buffer.append(" rhost=").append(ipaddr); 159 int i = 2; 160 while (ipaddrs.hasMoreElements()) { 161 ipaddr = (String) ipaddrs.nextElement(); 162 respVarbinds = (Vector) responses.get(ipaddr); 163 164 buffer.append("\n\t"); 165 buffer.append(printVars("respVarbinds" + i, respVarbinds)); 166 buffer.append(" rhost=").append(ipaddr); 167 i++; 168 } 169 } 170 return buffer.toString(); 171 } 172 173 /** 174 * Lets the observers know which source we received a response from. 175 */ 176 protected void tell_them() { 177 String sender = thisIP; 178 // if timed out then we are done waiting for replies. 179 if (isTimedOut()) { 180 sender = null; 181 } else { 182 // record this response for posterity 183 responses.put(sender, respVarbinds); 184 } 185 186 // tell all interested parties 187 notifyObservers(sender); 188 189 // free up space for next result 190 respVarbinds = null; 191 thisIP = null; 192 } 193 194 /** 195 * Fills in the received response. 196 * 197 * Now override fillin to fetch source ip address and not set answered 198 * you can get multiple responses by setting the timeout period long 199 * do this in the constructor. 200 * 201 * @param seq Description of Parameter 202 * @see Pdu#getResponseVarbinds() 203 */ 204 void fillin(AsnPduSequence seq) { 205 // this will be set to true (eventually) in handleNoAnswer() 206 if (answered) { 207 if (AsnObject.debug > 6) { 208 System.out.println(getClass().getName() + ".fillin(): " 209 + "Got a second answer to request " + getReqId()); 210 } 211 return; 212 } 213 214 // check that we haven't already heard from this host before: 215 thisIP = getContext().getReceivedFromHostAddress(); 216 if (responses.containsKey(thisIP)) { 217 if (AsnObject.debug > 6) { 218 System.out.println(getClass().getName() + ".fillin(): " 219 + "Got a second answer from " + thisIP 220 + " to request " + getReqId()); 221 } 222 return; 223 } 224 225 // fillin(null) can be called in case of a Decoding exception 226 if (seq != null) { 227 if (seq.isCorrect == true) { 228 int n = -1; 229 try { 230 // Fill in the request id 231 this.req_id = seq.getReqId(); 232 setErrorStatus(seq.getWhatError()); 233 setErrorIndex(seq.getWhereError()); 234 235 // The varbinds from the response/report are set in a 236 // new Vector. 237 AsnSequence varBind = seq.getVarBind(); 238 int size = varBind.getObjCount(); 239 respVarbinds = new Vector(size, 1); 240 for (n = 0; n < size; n++) { 241 Object obj = varBind.getObj(n); 242 if (obj instanceof AsnSequence) { 243 AsnSequence varSeq = (AsnSequence) obj; 244 try { 245 varbind vb = new varbind(varSeq); 246 respVarbinds.addElement(vb); 247 new_value(n, vb); 248 } catch (IllegalArgumentException exc) { 249 } 250 } 251 } 252 253 // At this point, I don't know whether I received a 254 // response and should fill in only the respVarbind or 255 // whether I received a request (via ListeningContext) 256 // and I should fill in the reqVarbinds. 257 // So when reqVarbinds is empty, I clone the 258 // respVarbinds. 259 if (reqVarbinds.isEmpty()) { 260 reqVarbinds = (Vector) respVarbinds.clone(); 261 } 262 } catch (Exception e) { 263 // it happens that an agent does not encode the varbind 264 // list properly. Since we try do decode as much as 265 // possible there may be wrong elements in this list. 266 267 DecodingException exc = new DecodingException( 268 "Incorrect varbind list, element " + n); 269 setErrorStatus(AsnObject.SNMP_ERR_DECODINGASN_EXC, exc); 270 } 271 } else { 272 // we couldn't read the whole message 273 // see AsnObject.AsnReadHeader, isCorrect 274 275 DecodingException exc = new DecodingException( 276 "Incorrect packet. No of bytes received less than packet length."); 277 setErrorStatus(AsnObject.SNMP_ERR_DECODINGPKTLNGTH_EXC, exc); 278 } 279 } 280 281 // always do 'setChanged', even if there are no varbinds. 282 setChanged(); 283 tell_them(); 284 clearChanged(); 285 286 // don't want to tell trans to stop since this will remove 287 // the PDU from the context. Instead depend on timeouts to 288 // free up the transmitter and remove PDU from context. 289 /* 290 * synchronized(this) 291 * { 292 * got = true; 293 * answered = true; 294 * notify(); // see also handleNoAnswer() 295 * if (trans != null) 296 * { 297 * // free up the transmitter, since 298 * // we are happy with the answer. 299 * // trans may be null if we are receiving a trap. 300 * trans.interruptMe(); 301 * } 302 * } 303 */ 304 } 305 306 }